我们知道类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。
Python的类成员还存在着一些具有特殊含义的成员,其中有一些比较重要的,我们一一来看:
(1) __init__
构造方法,通过类创建对象时,自动触发执行。
1 class Student(object):
2 def __init__(self, name):
3 self.name = name
4
5 ...
(2) __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
1 class Student(object):
2 ...
3
4 def __del__(self):
5 pass
(3) __call__
我们已经知道了类名后面加()是调用__init__方法,那对象后面加()呢?请看下面的代码:
1 class Student(object):
2 def __init__(self, name):
3 self.name = name
4
5 def __call__(self):
6 print(self.name)
7
8 ...
9
10 mike = Student("Mike")
11 mike()
程序输出是: Mike
(4) __dict__
有没有办法获取对象里所有普通字段及其值呢?Python为我们提供了__dict__:
1 class Student(object):
2 Nation = "China"
3
4 def __init__(self, name):
5 self.name = name
6 self.score = 0
7
8 ...
9
10 mike = Student("Mike")
11 print(mike.__dict__)
程序输出: {'score': 0, 'name': 'Mike'}
(5) __str__ , __repr__
对于一个对象,我们对其使用print时,默认程序输出的是对象的内存地址:
1 class Student(object):
2 def __init__(self, name):
3 self.name = name
4 self.score = 0
5
6 ...
7
8 mike = Student("Mike")
9 print(mike)
程序输出: <__main__.Student object at 0x0050F7B0>
怎么才能打印得好看呢?只需要定义好__str__()
方法,返回一个好看的字符串就可以了:
1 class Student(object):
2 def __init__(self, name):
3 self.name = name
4 self.score = 0
5
6 def __str__(self):
7 return self.name
8 ...
9
10 mike = Student("Mike")
11 print(mike)
程序输出就是__str__方法的返回值: Mike
如果我们把上面的代码在idle里运行,并敲下mike时(>>>mike),打印出来的还是对象的地址,
这是因为直接显示变量调用的不是__str__()
,而是__repr__()
,两者的区别是__str__()
返回用户看到的字符串,而__repr__()
返回程序开发者看到的字符串,也就是说,__repr__()
是为调试服务的。
解决办法是再定义一个__repr__()
。但是通常__str__()
和__repr__()
代码都是一样的,所以,有个偷懒的写法:
1 class Student(object):
2 def __init__(self, name):
3 self.name = name
4 self.score = 0
5
6 def __str__(self):
7 return self.name
8 __repr__ = __str__
(6) __iter__
如果一个类想被用于for ... in
循环,类似list或tuple那样,就必须实现一个__iter__()
方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()
方法拿到循环的下一个值,直到遇到StopIteration
错误时退出循环。
我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:
1 class Fib(object):
2 def __init__(self, maximum):
3 self.a = 0
4 self.b = 1 # 初始化两个计数器a,b
5 self.maximum = maximum # 定义停止循环的条件
6
7 def __iter__(self):
8 return self # 实例本身就是迭代对象,故返回自己
9
10 # Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到
11 # StopIteration错误时退出循环
12 def __next__(self):
13 self.a, self.b = self.b, self.a + self.b
14 if self.a > self.maximum:
15 raise StopIteration()
16 return self.a
17
18 for item in Fab(100):
19 print(item)
(7) __getitem__、__setitem__、__delitem__
上面的Fib类的对象虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第4个元素:
1 Fib(100)[4]
2 TypeError: 'Fib' object does not support indexing
要表现得像list那样按照下标取出元素,需要实现__getitem__()
方法
1 def __getitem__(self, i):
2 a, b = 0, 1
3 for x in range(i):
4 a, b = b, a + b
5 if a <= self.maximum:
6 return a
7 else:
8 raise ValueError("Out of range")
现在,就可以按下标访问数列的任意一项了: print("Fib(100)[4]:", Fib(100)[4]) ,输出是: Fib(100)[4]: 3
list还有个神奇的切片方法,Fib该如何实现呢?__getitem__()
传入的参数可能是一个int,也可能是一个切片对象slice
,所以要做判断:
1 def __getitem__(self, i):
2 if isinstance(i, int):
3 a, b = 0, 1
4 for x in range(i):
5 a, b = b, a + b
6 if a <= self.maximum:
7 return a
8 else:
9 raise ValueError("Out of range")
10 elif isinstance(i, slice):
11 if isinstance(i, slice): # i是切片
12 start = i.start
13 stop = i.stop
14 if start is None:
15 start = 0
16 a, b = 1, 1
17 L = []
18 for x in range(stop):
19 if x >= start:
20 L.append(a)
21 a, b = b, a + b
22 return L
但是没有对step参数作处理,也没有对负数作处理,所以,要正确实现一个__getitem__()
还是有很多工作要做的。
此外,如果把对象看成dict
,__getitem__()
的参数也可能是一个可以作key的object,例如str
。
与之对应的是__setitem__()
方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()
方法,用于删除某个元素:
1 def __getitem__(self, i):
2 if isinstance(i, int): # 对应于list, tuple的索引
3 a, b = 0, 1
4 for x in range(i):
5 a, b = b, a + b
6 if a <= self.maximum:
7 return a
8 else:
9 raise ValueError("Out of range")
10 elif isinstance(i, slice): # 对应于list,tuple的切片
11 if isinstance(i, slice): # i是切片
12 start = i.start
13 stop = i.stop
14 if start is None:
15 start = 0
16 a, b = 1, 1
17 L = []
18 for x in range(stop):
19 if x >= start:
20 L.append(a)
21 a, b = b, a + b
22 return L
23 elif isinstance(i, str): # 对应于字典的dic[key]或dic.get[key]
24 print(type(i))
25 else:
26 print("wrong type")
27
28 def __setitem__(self, key, value): # 对应于字典的dic[key] = value
29 print("key:value", key, value)
30
31 def __delitem__(self, key): # 对应于del dic[key]
32 print("del", key)