更加抽象
7.1 对象的魔力
7.1.1 多态
7.1.2 封装
7.1.3 继承
7.2 类和类型
7.2.1 类到底是什么
7.2.2 创建自己的类
# _*_ coding:utf8 _*_
__mateclass__=type#确定使用新式类
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
这个例子包含三个方法定义,除了它们是写在class语句里面外,一切都像是函数定义。Person当然是类的名字。class语句会在函数定义的地方创建自己的命名空间。一切看起来都挺好,但是那个self参数看起来有点奇怪。它是对于对象自身的引用。那么他是什么对象?创建一些实例看看:
foo=Person()
bar=Person()
foo.setName('Luke Skywalker')
bar.setName('Anakin Skywalker')
print foo.greet()
print bar.greet()
输出:
Hello,world! I'm Luke Skywalker.
None
Hello,world! I'm Anakin Skywalker.
None
好了,例子一目了然,应该能说明self的用处了。在调用foo的setName和greet函数时,foo自动将自己作为第一个参数传入函数中——因此形象地命名为self。对于这个变量,每个人可能都会有自己的叫法,但是因为它总是对象自身,所以习惯上总是叫做self。
显然这就是self的用处和存在的必要性。成员方法就没法访问他们要对其特性进行操作的对象本身了。
和之前一样,特性是可以在外部访问的:
print foo.name
bar.name='Yoda'
print bar.greet()
输出:
Luke Skywalker
Hello,world! I'm Yoda.
None
如果直到foo是Person的实例的话,那么还可以把foo.greet()看作Person.greet(foo)方便的简写。
7.2.3 特性、函数和方法
(在前面提到的)self参数事实上正是方法和函数的区别。方法(更专业一点可以称为绑定方法)将他们的第一个参数绑定到所属的实例上,因此你无需显示提供该参数。当然也可以将特性绑定到一个普通函数上,这样就不会有特殊的self参数了:
# _*_ coding:utf8 _*_
__mateclass__=type#确定使用新式类
class Class:
def method(self):
return 'I have a self!'
def function():
return "I don't..."
instance=Class()
print instance.method()
instance.method=function
print instance.method()
输出:
I have a self!
I don't...
注意,self参数并不依赖于调用方法的方式,前面我们使用的是instance.method的形式,可以随意使用其他变量引用同一个方法:
# _*_ coding:utf8 _*_
__mateclass__=type#确定使用新式类
class Bird:
song='Squaawk'
def sing(self):
return self.song
bird=Bird()
print bird.sing()
birdsong=bird.sing
print birdsong()
输出:
Squaawk
Squaawk
尽管最后一个方法调用看起来与函数调用十分相似,但是变量birdsong引用绑定方法bird.sing上,也就意味着这还是会对self参数进行访问。
再论私有化
默认情况下,程序可以从外部访问一个对象的特征。
有些程序员觉得这样做是可以的,但是有些人觉得这样就破坏了封装的原则。他们认为对象的状态对于外部应该是完全隐藏的。有人可能会奇怪为什么他们会站在如此极端的立场上。每个对象管理自己的特征不够吗?为什么还要对外部世界隐藏呢?毕竟如果能直接使用CloseObject和name特征的话就不用使用setName和getName方法了。
关键在于其他程序员可能不知道你的对象的具体操作。例如,CloseObject可能会在其他对象更改自己的名字的时候,给一些管理员发送邮件消息。这应该是setName方法的一部分。但是如果直接用c.name设定名字会发生什么?什么都没发生,Email也没发出去。为了避免这类事情的发生,应该使用是有特征,这是外部对象无法访问,但getName和setName等访问器能够访问的特征。
Python并不直接支持私有方式,而要靠程序员自己把握在外部进行特征修改的时机。毕竟在使用对象前应该如何使用。但是,可以用一些小技巧达到私有特性的效果。
为了让方法或者特征变为私有,只要在它的名字面前加上双下划线即可:
class Secretive:
def __inaccessible(self):
print "Bet you can't see me..."
def accessible(self):
print "The secret message is:"
self.__inaccessible()
s = Secretive()
s.accessible()
输出:
The secret message is:
Bet you can't see me...
__inaccessible从外界无法访问,而在类内部还能使用访问。
尽管双下划线有些奇怪,但是看起来像是其他语言中的标准私有方法。真正发生的事情才不是标准的。类的内部定义中,所有双下划线开始的名字都被“翻译”成前面家伙是那个单下划线和类名的形式。
所以,可以这样访问:
s._Secretive__inaccessible()
7.2.4 类的命名空间
7.2.5 指定超类
#_*_ coding:utf8 _*_
class Filter:
def init(self):
self.blocked = []
def filter(self,sequence):
return [x for x in sequence if x not in self.blocked]
class SPAMFilter(Filter):#SPAMFilter是Filter的子类
def init(self):#重写Filter超类中的init方法
self.blocked = ['SPAM']
f = Filter()
f.init()
print f.filter([1,2,3])
s = SPAMFilter()
s.init()
print s.filter(['SPAM','SPAM','SPAM','SPAM','eggs','bacon','SPAM'])
输出:
['eggs', 'bacon']
7.2.6 检查继承
#_*_ coding:utf8 _*_
class Filter:
def init(self):
self.blocked = []
def filter(self,sequence):
return [x for x in sequence if x not in self.blocked]
class SPAMFilter(Filter):#SPAMFilter是Filter的子类
def init(self):#重写Filter超类中的init方法
self.blocked = ['SPAM']
print issubclass(SPAMFilter,Filter)
print issubclass(Filter,SPAMFilter)
输出:
False
7.2.7 多个超类
class Calculator:
def calculate(self,expression):
self.value = eval(expression)
class Talker:
def talk(self):
print 'Hi, my value is',self.value
class TalkingCalculator(Calculator,Talker):
pass
tc = TalkingCalculator()
tc.calculate('1+2*3+897345')
tc.talk()
输出:
7.2.8 接口和内省
7.3 一些关于面向对象设计的思考