魔法方法和属性
在python中,有的名称会在前面和后面都加上两个下划线,这种写法很特别,同时表示名字有特殊含义,所以绝不要在自己的程序中使用这种名字。
在python中,这种名字组成的集合所包含的方法称为魔法(或特殊)方法。
如果对象实现了这些方法中的某一个,那么这个方法会在特殊的情况下(确切的说是根据名字)被python调用。
很久以前(Python2.2中),对象的工作方式就有了很大的变化。这种改变产生了一些影响,但对于刚开始使用python的人来说,大多数都不重要。
值得注意的是,尽管可能是用的是新版的python,但有一些特性不会在老式类上起作用。
为了确保类是新型的,应该把赋值语句__metaclass__=type放在你的模块的最开始。或者(直接或者间接)子类化内建类object。
1.构造方法
首先要讨论的第一个魔法方法是构造方法。构造方法是一个很奇特的名字,它代表着类似于以前例子中使用过的那种名为init的初始化方法。
但构造方法和其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法。
在python中创建一个构造方法很容易。只要把init方法的名字从简单的init修改为魔法版本的__init__即可:
>>> class Foobar: ... def __init__(self): ... self.somevar = 42 ... >>> >>> f = Foobar() >>> f.somevar 42
如果要给构造方法传几个参数的话,会发生什么情况了?
>>> class Foobar: ... def __init__(self,value=42): #参数可选 ... self.somevar = value #有点类似于函数的传参 ...
参数可选,传个参数试试
>>> f = Foobar('This is a constructor argument') >>> f.somevar 'This is a constructor argument'
在所有的魔法方法中,__init__是使用最多的一个。
2.重写一般方法和特殊的构造方法
在继承的知识中,每个类都有可能拥有一个或者多个超类,它们从超类那里继承行为方式。
如果一个方法在B类的一个实力被调用(或一个属性被访问),但在B类中没有找到方法,那么就会取它的超类里面找。
>>> class A: ... def hello(self): ... print("Hello,I'm A.") ... >>> class B(A): ... pass ... >>> a = A() >>> b =B() >>> a.hello() Hello,I'm A. >>> b.hello() #A继承了一个叫hello的方法,被B继承。 Hello,I'm A.
因为B类没有自己的构造方法,所以当hello被调用时,原始的信息就被打印出来。
在子类中增加功能的最基本的方法是增加方法,但是也可以重写一些超类的方法来定义继承的行为。
>>> class B(A): ... def hello(self): ... print("Hello,I") ... >>> b.hello() Hello,I
重写是继承机制中的一个重要内容,对于构造方法尤为重要。
构造方法用来初始化新创建对象的状态,大多数字类不仅要拥有自己的初始化代码,还要拥有超类的初始化代码。
虽然重写的机制对于所有方法都是一样的,但是当处理构造方法比重写普通方法时,更可能遇到特别的问题:如果一个类的构造方法被重写,那么就需要调用超类(你所继承的类)的构造方法,否则可能不会被正确的初始化。
考虑下面的Bird类:
class Bird: def __init__(self): self.hungry = True def ear(self): if self.hungry : print('Aaaah...') self.hungry = False else: print('No,thanks')
定义了鸟最基本的能力——吃,
Aaaah...
No,thanks
吃过之后,它就不再饥饿了,加入子类SongBird。
class SongBird(Bird): def __init__(self): self.sound = 'Squawk' def sing(self): print(self.sound)
一样可以生成对象,并且调用其中的方法。
sb = SongBird()
sb.sing()
结果:
Squawk
SongBird是Bird的子类,能否继承其中的方法了?试一下
sb.ear() 结果: Traceback (most recent call last): File "I:/untitled/cx/11月/函数.py", line 23, in <module> sb.ear() File "I:/untitled/cx/11月/函数.py", line 5, in ear if self.hungry : AttributeError: 'SongBird' object has no attribute 'hungry'
Why?Why?...
异常很清楚的说明了错误:SongBird没有hungry的特性。
原因是这样的:在SongBird中,构造方法被重写,但新的构造方法没有任何关于hungry特性的代码。
为了达到预期的效果,SongBird的构造方法必须被重写,有两种方法能够达到这一目的:调用未绑定的构造方法,或者使用super函数。
3.调用未绑定的超类的构造方法
调用超类的构造方法其实很简单,只需要在刚才的例子上小小的改动。
class SongBird(Bird): def __init__(self): Bird.__init__(self) #就是如此简单 self.sound = 'Squawk' def sing(self): print(self.sound)
结果:
sb.ear()
Aaaah...
为什么会有这样的结果了?在调用一个实例方法的时候,该方法的self参数会被自动绑定到实例上(这称为绑定方法)。
如果直接调用类的方法(比如:Bird.__init__),那么就没有实例会被绑定。
这样就可以自由的提供需要的self参数了,这样的方法称为未绑定的方法。
4.使用super函数
如果你不想坚守旧版本的Python阵营,也可以使用super函数,但是它只能在新式类中使用。当前的类和对象可以作为super函数的参数使用,调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法。
class SongBird(Bird): def __init__(self): super(SongBird,self).__init__() self.sound = 'Squawk' def sing(self): print(self.sound) sb = SongBird() sb.ear() sb.ear( 结果: Aaaah... No,thanks