一、常见魔法方法
python类中有一类特殊内建方法,可以通过在类中定义这些方法,实现类似于装饰器一样的行为,改变类本身的默认行为,python称这些行为“magic method”
常见的“magic method”有如下几种:
__str__
与__repr__
__getattr__
与__getattribute__
__setattr__
与__delattr__
__iter__
与__getitem__
__init__
与__new__
二、__str__
与 __repr__
__str__
相当于str()方法的调用,__repr__
相当于repr()方法调用。str是针对于让人更好的理解的字符串格式化,主要适用于定义对类实例直接进行print()方法时输出;而repr是让机器更好理解的字符串格式化,定义在IDE中直接调用类是输出的字符串
1 | class A(): |
三、__getattr__
与 __getattribute__
python手册中对这两种方法的描述如下:
object.__getattr__(self, name)
Called when the default attribute access fails with an AttributeError (either__getattribute__()
raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or__get__()
of a name property raises AttributeError). This method should either return the (computed) attribute value or raise an AttributeError exception.Note that if the attribute is found through the normal mechanism,__getattr__()
is not called. (This is an intentional asymmetry between__getattr__()
and__setattr__()
.) This is done both for efficiency reasons and because otherwise__getattr__()
would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the__getattribute__()
method below for a way to actually get total control over attribute access.
object.__getattribute__(self, name)
Called unconditionally to implement attribute accesses for instances of the class. If the class also defines__getattr__()
, the latter will not be called unless__getattribute__()
either calls it explicitly or raises an AttributeError. This method should return the (computed) attribute value or raise an AttributeError exception. In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example,object.__getattribute__(self, name)
.
上述描述表示,当用户在调用实例属性时,会首先无条件调用getattribute方法查找其属性并返回对应的value。
而getattr方法只有在访问实例属性失败时,即getattribute方法查找失败时才会调用,一旦实例属性被定义,再次查找属性会由getattribue提供返回值,不再调用getattr方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Person:
def __getattr__(self, attr):
print('call __getattr__')
if attr == 'name':
return 'lol no name'
elif attr == 'age':
return 'don\'t tell U'
def __getattribute__(self, attr):
print('call __getattribute__')
# return self.name # self.name会调用__getattribute__方法,这个返回会导致无限嵌套调用。
return super(Person, self).__getattribute__(attr)
s1 = Person()
print(s1.name)
print('---')
s1.name = 'Maria'
print(s1.name)
运行结果:
call __getattribute__ # 查找实例属性时,先调用__getattribute__查找
call __getattr__ # __getattribute__查找失败后调用__getattr__
lol no name
---
call __getattribute__
Maria
四、__setattr__
与 __delattr__
__setattr__
与__delattr__
分别在对实例属性进行设置和删除的时候调用,在重写这类方法时需要注意上述例子中无限嵌套循环调用。
1 | class Person(): |
五、__iter__
与 __getitem__
__iter__
返回一个迭代器,可用于对类实例进行迭代,可通过for循环遍历;__getitem__
用于实现对实例以dict[item]或者list[index]的方式获取类属性,即x.__getitem__(y) <=> x[y]
。
当类仅实现了__getitem__
方法而没有实现__iter__
时,for循环会默认初始化一个int类型的index,默认由0开始,并作为__getitem__
的参数,若__getitem__
重写为可以通过这个index找到相应的属性值,则此时没有__iter__
方法的实例也可用于for循环,但导入collections模块中的Iterable和Iterator之后使用isinstance判断发现,此时实例即不是Iterable也不是Iterator
1 | from collections import Iterable, Iterator |
六、__init__
与 __new__
这两个方法在实例初始化时按顺序调用,当初始化一个实例时,解释器先调用__new__()
生成一个实例对象,然后根据__init__()
来初始化实例。通过如下例子观察两个函数的执行顺序:
1 | class Person(): |