在Python中,有些名称很特别,开头和结尾都是两个下划线。你在本书前面已经见过一些, 如__future__。
在这样的名称中,很大一部分都是魔法(特殊)方法的名称。如果你的对象实现了这些方法,它们将在特定情况下(具体是哪种情况取决于方法的名称)被Python调用,而几乎不需要直接调用。
本章讨论几个重要的魔法方法,其中最重要的是__init__以及一些处理元素访问的方法(它 们让你能够创建序列或映射)。本章还将讨论两个相关的主题:特性(property)和迭代器(iterator)。
9.1 构造函数
我们要介绍的第一个魔法方法是构造函数。你可能从未听说过构造函数(constructor),它其 实就是本书前面一些示例中使用的初始化方法,只是命名为__init__。然而,构造函数不同于普 通方法的地方在于,将在对象创建后自动调用它们。
不需要像下面这样:
f = FooBar()
f.init()
只需要
f = FooBar()
在Python中,创建构造函数很容易,只需将方法init的名称从普通的init改为魔法版__init__ 即可。
class FooBar:
def __init__(self):
self.somevar = 42
f = FooBar()
f.somevar
# 42
9.1.1 重写普通方法和特殊的构造函数
第7章介绍了继承。每个类都有一个或多个超类,并从它们那里继承行为。对类B的实例调用方法(或访问其属性)时,如果找不到该方法(或属性),将在其超类A中查找。
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()
# Hello, I'm A.
由于类B自己没有定义方法hello,因此对其调用方法hello时,打印的是消息"Hello, I’m A."。
下面重写类B中的方法hello
class B(A):
def hello(self):
print("Hello, I'm B.")
b.hello()
# Hello, I'm B.
重写构造函数时,必须调用超类(继承的类)的构造函数,否则可能无法正确地初始化对象。
class Bird: # 父类
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print('Aaaah ...')
self.hungry = False
else:
print('No, thanks!')
bird = Bird()
bird.eat()
bird.eat()
class SongBird(Bird): # 子类
def __init__(self):
self.sound = 'Squawk!'
def sing(self):
print(self.sound)
sb = SongBird()
sb.sing()
sb.eat()
# 报错 'SongBird' object has no attribute 'hungry'
因为在SongBird中重写了构造函数,但新的构造函数没有包含任何初始化属性hungry的代码。
要消除这种错误,SongBird的构造函数必须调用其超类(Bird)的构造函数,以确保基本的初始化得以执行。
为此,有两种方法:调用未关联的超类构造函数,以及使用函数super。
9.1.2 调用未关联的超类构造函数
class SongBird(Bird): # 子类
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk!'
def sing(self)