当通过未定义的属性名称和实例通过点号进行访问时,就会用属性名称作为字符串调用这个方法,但如果类使用了继承,并且在超类中可以找到这个属性,那么就不会触发。
>>> class empty:
... def __getattr__(self, item):
... if item == 'age':
... return 40
... else:
... raise AttributeError(item)
...
>>>
>>> x = empty()
>>> print(x.age)
40
>>> print(x.name)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __getattr__
AttributeError: name
>>> class accesscontrol:
... def __setattr__(self, key, value):
... if key == 'age':
... self.__dict__[key] = value
... else:
... raise AttributeError(key + ' not allowed')
...
>>>
>>> x = accesscontrol()
>>> x.age = 40
>>> print(x.age)
40
>>> x.name = 'Hello'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
AttributeError: name not allowed
__repr__和__str__会返回字符串表达式__repr__和__str__都是为了更友好的显示,具体来说,如果在终端下print(Class)则会调用__repr__,非终端下会调用__str__方法,且这两个方法只能返回字符串;
class adder:
def __init__(self, value=0):
self.data = value
def __add__(self, other):
self.data += other
def __repr__(self):
return 'addrepr(%s)' % self.data
def __str__(self):
return 'N: %s' % self.data
x = adder(2)
x + 1
print(x)
print((str(x), repr(x)))
右侧加法和原处加法: __radd__和__iadd__只有当+右侧的对象是类实例,而左边对象不是类实例的时候,Python才会调用__radd__
class Commuter:
def __init__(self, val):
self.val = val
def __add__(self, other):
print('add', self.val, other)
return self.val + other
def __radd__(self, other):
print('radd', self.val, other)
return other + self.val
x = Commuter(88)
y = Commuter(99)
print(x + 1)
print('')
print(1 + y)
print('')
print(x + y)
使用__iadd__进行原处加法
class Number:
def __init__(self, val):
self.val = val
def __iadd__(self, other):
self.val += other
return self
x = Number(5)
x += 1
x += 1
print(x.val)
class Number:
def __init__(self, val):
self.val = val
def __add__(self, other):
return Number(self.val + other)
x = Number(5)
x += 1
x += 1
print(x.val)
Call表达式:__call__当调用类实例时执行__call__方法
class Callee:
def __call__(self, *args, **kwargs):
print('Callee:', args, kwargs)
C = Callee()
C(1, 2, 3)
C(1, 2, 3, x=1, y=2, z=3)
class Prod:
def __init__(self, value):
self.value = value
def __call__(self, other):
return self.value * other
x = Prod(3)
print(x(3))
print(x(4))
比较:__lt__,__gt__和其他方法类可以定义方法来捕获所有的6种比较运算符:<、>、<=、>=、==和!=
class C:
data = 'spam'
def __gt__(self, other):
return self.data > other
def __lt__(self, other):
return self.data < other
x = C()
print(x > 'han')
print(x < 'han')
布尔值测试:bool和len
class Truth:
def __bool__(self):
return True
X = Truth()
if X: print('yes')
class Truth:
def __bool__(self):
return False
X = Truth()
print(bool(X))
如果没有这个方法,Python退而求其次的求长度,因为一个非空对象看作是真:
>>> class Truth:
... def __len__(self): return 0
...
>>> X = Truth()
>>> if not X: print('no')
...
no
如果两个方法都有,__bool__会胜过__len__:
>>> class Truth:
... def __bool__(self): return True
... def __len__(self): return 0
...
>>> X = Truth()
>>> bool(X)
True
如果两个方法都没有定义,对象毫无疑义的看作为真:
>>> class Truth: pass
...
>>> bool(Truth)
True
对象解析函数:__del__每当实例产生时,就会调用init构造函数,每当实例空间被收回时,它的对立面__del__,也就是解析函数,就会自动执行;
class Life:
def __init__(self,):
print('Hello, ', name)
self.name = name
def __del__(self):
print('Goodbye', self.name)
brian = Life('Brian')