构造方法

当一个对象创建时,会自动调用构造方法

In [4]:
class FooBar:
    def __init__(self):           #创建一个名为__init__的方法,它会自动成为构造方法
        self.somevar = 42
In [5]:
f  = FooBar()
In [6]:
f.somevar
Out[6]:
42
In [7]:
#其实,这个方法还能加入参数
class FooBar2:
    def __init__(self, value = 42):
        self.somevar = value
In [8]:
f1 = FooBar()
In [10]:
f1.somevar
Out[10]:
42
In [12]:
#怎么给出value的值呢?直接在类名的括号内给出就可以了
f2 = FooBar2(24)
In [13]:
f2.somevar
Out[13]:
24

init相对的还有一个del方法
它在对象要被垃圾回收之前调用,具体时间是不可预料的,应该尽量避免

构造方法重写问题

假设存在一个类A和它的子类B,两个类都写有各自的构造方法
那么B的构造方法就会被重写而覆盖掉A的构造方法
那么初始化的时候并没有继承超类的初始化
而往往我们又需要它继承超类的初始化,然后自己添加需要的新的初始化
这时候可以通过两个方法解决——调用超类构造方法的未绑定版本、使用super函数

In [16]:
class Bird:
    def __init__(self):
        self.hungry = True
    def eat(self):
        if self.hungry:
            print 'Ahhhh......'
            self.hungry = False
        else:
            print 'No, thanks!'
#定义一个基类Bird,它会自动初始化为hungry,它有一个方法eat,会判断自己是否hungry来选择行为

调用超类构造方法的未绑定版本

主要用于旧式类,看懂就好
一般在调用一个实例时,self会自动绑定到实例自身,称为绑定方法
但是我们也可以手动给出self的值,称为未绑定(unbound)方法

In [20]:
class SongBird(Bird):
    def __init__(self):
        Bird.__init__(self)     #如果去掉这行,那么由于__init__方法的重写,并不会执行self.hungry = True
        #超类.__init__(self)
        self.sound = 'Squawk'
    def sing(self):
        print self.sound
#定义一个Bird的子类SongBird,它会继承超类的初始化,并且添加上自己的初始化sound,并且具有方法sing
#注意第三行代码

注意第三行self,它不是Bird自身,而是SongBird(的实例)

使用super函数

只能用于新式类(必须在模块或者程序的开头加入赋值语句“__metaclass__=type”),要掌握
优点——

  1. 直观
  2. 智能,可以继承超类的超类(但是要确保超类也使用了super函数)
    如果你只想继承超类,而不想继承超类的超类,拿还是使用旧式类的方法吧
    其原理比较复杂,无需细究,会用就好
In [19]:
class SongBird(Bird):
    def __init__(self):
        super(SongBird, self).__init__()        #把第三行改成这样
        #super(子类,self).__init__()
        self.sound = 'Squawk'
    def sing(self):
        print self.sound

常见魔法方法——创建行为类似与序列或映射的对象

规则(protocol):描述管理某种形式行为的规则,说明了应该实现何种方法以及这些方法做什么
python中的多态性是基于对象的行为,而不是祖先的行为
其他语言中对象可能被要求属于某个类,或者实现某个借口,但是python只要求对象遵循给定的规则即可

基本的序列和映射规则

若对象可变,那么需要使用两个魔法方法;
若对象不可变,那么需要使用四个魔法方法
(假设我们创建了一个类似序列或映射的对象a)

  • __len__(self):返回集合中项目的数量
    如果该方法返回0,且没有实现重写该行为的__nonzero__,那么这个对象会被作为False
    执行诸如len(a)的操作会调用该方法
  • __getitem__(self, key):返回所给键的对应值
    执行诸如a[2]的操作会调用该方法
    如果是序列,键是整数0~n-1; 如果是映射,键可以是任意种类
  • __setitem__(self, key, value):按照一定的方式存储key和相关的value
    执行诸如a[2] = 2的操作时会调用该方法
  • __delitem__(self, key):使用del语句时会被调用,删除对象
    执行诸如del a[2]时会调用该方法

  • 附加要求——

    1. 如果键是负整数,表示从末尾计数
    2. 如果键是不合适的类型,会引发TypeError
    3. 如果序列索引是正确类型,但是超出范围,会引发IndexError
In [22]:
#接下来我们来模拟无穷序列的相关操作  
In [23]:
def checkIndex(key):
    '''
    检查所给的键是否是能接受的索引
    键应该是非负整数
    如果不是整数,引发TypeError
    如果是负数,引发IndexError(因为这是一个无穷序列)
    '''
    if not isinstance(key, (int, long)): raise TypeError     #如果key不是int或long类型,引发TypeError
                      #这里使用的isinstance函数,这个函数应该避免使用的,但是为了遵循序列的规则,我们不得不使用它
    if key < 0: raise IndexError
In [24]:
class ArithmeticSequence:
    def __init__(self, start = 0, step = 1):
        '''
        初始化算术序列
        start:序列的第一个值
        step:步长
        changed:用户修改的值的字典
        '''
        self.start = start
        self.step = step
        self.changed = {}
    def __getitem__(self, key):
        '优先去检查用户修改的项,否则计算对应的值'
        checkIndex(key)
        
        try:
            return self.changed[key]     #尝试去取这个值
        except KeyError:    #如果键错误
            return self.start + key * self.step      #返回计算值
    def __setitem__(self, key, value):
        checkIndex(key)
        
        self.changed[key] = value
In [25]:
s = ArithmeticSequence(1, 2)      #参数1,2分别传递给start和step的初始化
In [27]:
s[4]
Out[27]:
9
In [28]:
s[4] = 2
In [29]:
s[4]
Out[29]:
2
In [30]:
s[5]
Out[30]:
11
In [31]:
s.changed
Out[31]:
{4: 2}
In [32]:
del s[4]     #我们没有给出__delitem__方法,所以会报错
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-32-85b7a7e2e37f> in <module>()
----> 1 del s[4]     #我们没有给出__delitem__方法,所以会报错

AttributeError: ArithmeticSequence instance has no attribute '__delitem__'
In [33]:
len(s)      #我们没有给出__len__方法,所以也会报错
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-33-c872e39408df> in <module>()
----> 1 len(s)      #我们没有给出__len__方法,所以也会报错

AttributeError: ArithmeticSequence instance has no attribute '__len__'

子类化列表,字典,字符串

如果我们要写一个类,它具有列表的大部分功能,但是又要修改一部分功能
就可以把这个类,作为list类型的子类,然后重写你所需要修改的方法(这个行为称为子类化)
相应的字典的类型是dict,字符串的类型是string

In []: