Python面向对象-特殊成员
我们知道类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。
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 ,我们可以看到对象()会调用__call__方法,我们可以通过callable()函数判断对象是否可以调用
(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'} ,对于静态字段Nation = "China",__dict__内并不包含
(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)