在类范围内动态覆盖列表的内置函数 [英] Override list's builtins dynamically in class scope

查看:84
本文介绍了在类范围内动态覆盖列表的内置函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

纯粹是好奇心问题:

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 __ 吗?

If I use setattr(m, 'append', r) then m.append(2) will fail. So is __iadd__ called on the class object ?

我也可以使用 settattr 从类范围?天真的尝试,例如:

Also can I use settattr from class scope ? A naive attempt as in:

def r(*args, **kwargs): raise Exception('hop')
class Li(list):
    for s in {'__iadd__','append'}: setattr(Li, s, r)

失败,出现 NameError:名称'Li'未定义

推荐答案

在新样式类中,实例 __ getattr __ 方法不再拦截内置操作所进行的调用。使用内置操作时,搜索从类级别而不是实例级别开始。不过,对方法名称的显式调用将起作用:

In new style classes, the instance __getattr__ method no longer intercepts calls made by built-in operations. When the built-in operation is used, the search begins at the class level instead of at the instance level. Nevertheless, an explicit call to the method name will work:

>>> 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

实现第二个目标的一种方法是使用元类,尤其是如果您只想创建属性一次(根据您的评论):

One way to accomplish your second goal is to use a metaclass, especially if you only want to create the attributes once (as per your comments):

>>> 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>})

这篇关于在类范围内动态覆盖列表的内置函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆