纯粹的好奇心问题:
class Li(list): pass
m, n= Li([1]), Li([2])
def r(*args, **kwargs): raise Exception('hop')
setattr(m, '__iadd__', r)
m += n
print m # [1, 2]
setattr(Li, '__iadd__', r)
m += n
输出:
[1, 2]
Traceback (most recent call last):
File "C:\...\test_override.py", line 8, in <module>
m+=n
File "C:\...\test_override.py", line 3, in r
def r(*args, **kwargs): raise Exception('hop')
Exception: hop
如果我使用setattr(m, 'append', r)
,则m.append(2)
将失败。那么__iadd__
是否在类对象上调用了?
我也可以在课程范围内使用settattr
吗?一个天真的尝试,如:
def r(*args, **kwargs): raise Exception('hop')
class Li(list):
for s in {'__iadd__','append'}: setattr(Li, s, r)
因错误而错误 NameError:name&#39; Li&#39;未定义
答案 0 :(得分:2)
在新样式类中,实例__getattr__
方法不再拦截内置操作发出的调用。使用内置操作时,搜索从类级别开始,而不是在实例级别。然而,对方法名称的显式调用将起作用:
>>> class Li(list): pass
>>> m, n= Li([1]), Li([2])
>>> def r(*args, **kwargs):
raise Exception('hop')
>>> setattr(m, '__iadd__', r)
>>> m += n # no exception raised
>>> m.__iadd__(n) # explicitly calling the method searches the instance first
Traceback (most recent call last):
File "<pyshell#76>", line 1, in <module>
m.__iadd__(n)
File "<pyshell#73>", line 1, in r
def r(*args, **kwargs): raise Exception('hop')
Exception: hop
实现第二个目标的一种方法是使用a metaclass,特别是如果您只想创建属性一次(根据您的评论):
>>> class Meta(type):
def __new__(meta, name, bases, attrdict):
print 'adding class attributes!'
for s in {'__iadd__','append'}:
attrdict[s] = r
return super(Meta, meta).__new__(meta, name, bases, attrdict)
def __init__(cls, name, bases, attrdict):
super(Meta, cls).__init__(name, bases, attrdict)
>>> class Li(list):
__metaclass__ = Meta
adding class attributes! # attributes added here
>>> t = Li() # but not on instance creation
>>> Li.__dict__
dict_proxy({'__module__': '__main__', '__metaclass__': <class '__main__.Meta'>, '__iadd__': <function r at 0x02AAA270>, '__dict__': <attribute '__dict__' of 'Li' objects>, '__weakref__': <attribute '__weakref__' of 'Li' objects>, '__doc__': None, 'append': <function r at 0x02AAA270>})