7.1对象的魔力
面向对象程序设计中的术语对象(object)基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。使用对象替代全局变量和函数的原因可能有很多。其中对象最重要的优点包括以下几方面。
多态(Polymorphism):意味着可以对不同类的对象使用同样的操作,它们会像被“施了魔法一般”工作。
封装(Encapsulation ):对外部世界隐藏对象的工作细节。
继承(Inheritance ):以普通的类为基础建立专门的类对象。
7.1.1多态
1.多态和方法
绑定到对象特性上面的函数称为方法(method ).
>>> [1,2,"a"].count('a') 1
对于变量x来说,不需要知道它是字符串还是列表,就可以调用它的count方法--不用管它是什么类型(只要你提供了一个字符作为参数即可)。
>>> from random import choice >>>x=choice(['hello,world!',[1,2,'e','e',4]]) >>> x.count('e') 2
关键点在于不需要检测类型:只需要知道x有个叫做count的方法,带有一个字符作为参数,并且返回整数值就够了。如果其他人创建了的对象类也有count方法,那也无所谓—你只需要像用字符串和列表一样使用该对象就行了。
2.多态的多种形式
>>> def add(x,y): return x+y >>> add('fish','license') 'fishlicense'
参数可以是任何支持加法的对象。如果需要编写打印对象长度消息的函数,则只需对象具有长度(len函数可用)即可:
>>> add('fish','license') 'fishlicense' >>> def length_message(x): ... print "the length of",repr(x),"is",len(x)
函数中使用了repr函数,repr函数是多态特性的代表之一,可以对任何东西使用:
>>>length_message("fnord") the length of 'fnord' is 5 >>> length_message([1,2,3]) the length of [1, 2, 3] is 3
要使用多态函数和运算符,多态就“消除”了。事实上,唯一能够毁掉多态的就是使用函数显示地检查类型,比如type, isinstance以及issubclass函数等。如果可能的话,应该尽力避免使用这些毁掉多态的方式。真正重要的是如何让对象按照你所希望的方式工作,不管它是否是正确的类型(或者类)。
7.1.2封装
封装是对全局作用域中其他区域隐藏多余信息的原则。听起来有些像多态一一使用对象而不用知道其内部细节,两者概念类似,因为它们都是抽象的原则一一它们都会帮助处理程序组件而不用过多关心多余细节,就像函数做的一样。
但是封装并不等同于多态。多态可以让用户对于不知道是什么类(或对象类型)的对象进行方法调用,而封装是可以不用关心对象是如何构建的而直接进行使用。听起来还是有些相似?让我们用多态而不用封装写个例子,假设有个叫做OpenObject的类:
>>> o=OpenObject() >>> o.setName('sir lancelot') >>> o.getName() ‘sir lancelot’
假设变量0将它的名字存储在全局变量globalName中:
>>>globeName ‘sir lancelot’
这就意味着在使用OpenObject类的实例时候,不得不关心globalName的内容。实际上要确保不会对它进行任何更改:
>>>globaName=’sir gumby’ >>>o.getName() ‘sir gumby’
如果创建多个openobject实例就会出问题
7.2类和类型
7.2.1类到底是什么
所有的对象都属于某一个类,称为类的实例(instance)
7.2.2创建自己的类
>>> class Person: def setName(self,name): self.name=name def getName(self): return self.name def greet(self): print "hello world,i'm %s,"% self.name >>> foo=Person() >>> bar=Person() >>> foo.setName('luke skywalker') >>> bar.setName('anakinskywalker') >>> foo.greet() hello world,i'm luke skywalker, >>> bar.greet() hello world,i'm anakin skywalker,
在调用foo的setName和greet函数时,foo自动将自己作为第一个参数传入函数中—因此形象地命名为self。对于这个变量,每个人可能都会有自己的叫法,但是因为它总是对象自身,所以习惯上总是叫做self
特性是可以在外部访问的:
>>> foo.name 'luke skywalker' >>> bar.name='Yoda' >>> bar.greet() hello world,i'm Yoda,
7.2.3特性、函数和方法
self参数事实上正是方法和函数的区别。方法(更专业一点可以称为绑定方法)将它们的第一个参数绑定到所属的实例上,因此这个参数可以不必提供。所以可以将特性绑定到一个普通函数上,这样就不会有特殊的self参数了:
>>> class Class: ... def method(self): ... print 'i have a self!' ... >>> def function(): ... print "i don't..." ... >>> instance=Class() >>> instance.method() i have a self! >>> instance.method=function >>> instance.method() i don't...
self参数并不取决于调用方法的方式,目前使用的是实例调用方法,可以随意使用引用同一个方法的其他变量:
>>> class Bird: ... song='squaawk!' ... def sing(self): ... print self.song ... >>> bird=Bird() >>> bird.sing() squaawk! >>> birdsong=bird.sing >>> birdsong() squaawk!
最后一个方法调用看起来与函数调用十分相似,但是变量birdsong引用绑定方法bird.sing上,也就意味着这还是对self参数的访问。
默认情况下,程序可以从外部访问一个对象的特性。再次使用前面讨论过的有关封装的例子:
>>>c .name ‘sir lancelot’ >>>c .name=’sir gumby’ >>>c.getName() ‘sir gumby'
为了让方法或者特性变为私有(从外部无法访问),只要在它的名字前面加上双下划线即可:
>>> class Secretive: ... def __inaccessible(self): ... print "bet you can't see me" ... def accessible(self): ... print "the secret message is:" ... self.__inaccessible()
现在__inaccessible从外界是无法访问的,而在类内部还能使用(比如从accessible)访问:
>>> s=Secretive() >>> s._Secretive() Traceback (most recent call last): File "<input>", line 1, in <module> AttributeError: 'Secretive' object has noattribute '_Secretive' >>> s.accessible() the secret message is: bet you can't see me
类的内部定义中,所有以双下划线开始的名字都被“翻译”成前面加上单下划线和类名的形式。