魔法方法和属性

在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

 

posted @ 2017-12-20 15:40  明王不动心  阅读(285)  评论(0编辑  收藏  举报