我怎样才能拦截在新的风格类中调用python的“魔术”方法?
我试图在新的风格类中拦截调用python的双下划线魔术方法。 这是一个微不足道的例子,但它显示的意图是:
class ShowMeList(object): def __init__(self, it): self._data = list(it) def __getattr__(self, name): attr = object.__getattribute__(self._data, name) if callable(attr): def wrapper(*a, **kw): print "before the call" result = attr(*a, **kw) print "after the call" return result return wrapper return attr
如果我在列表周围使用代理对象,我得到非魔术方法的预期行为,但是我的包装函数永远不会调用魔术方法。
>>> l = ShowMeList(range(8)) >>> l #call to __repr__ <__main__.ShowMeList object at 0x9640eac> >>> l.append(9) before the call after the call >> len(l._data) 9
如果我不从对象inheritance(第一行class ShowMeList:
一切都按预期工作:
>>> l = ShowMeList(range(8)) >>> l #call to __repr__ before the call after the call [0, 1, 2, 3, 4, 5, 6, 7] >>> l.append(9) before the call after the call >> len(l._data) 9
我如何用新的风格类来完成这个拦截?
出于性能原因,Python总是在类(和父类) __dict__
寻找魔术方法,而不使用普通属性查找机制。 解决方法是在类创build时使用元类自动添加魔术方法的代理; 例如,我已经使用这种技术来避免为包装类编写样板调用方法。
class Wrapper(object): """Wrapper class that provides proxy access to an instance of some internal instance.""" __wraps__ = None __ignore__ = "class mro new init setattr getattr getattribute" def __init__(self, obj): if self.__wraps__ is None: raise TypeError("base class Wrapper may not be instantiated") elif isinstance(obj, self.__wraps__): self._obj = obj else: raise ValueError("wrapped object must be of %s" % self.__wraps__) # provide proxy access to regular attributes of wrapped object def __getattr__(self, name): return getattr(self._obj, name) # create proxies for wrapped object's double-underscore attributes class __metaclass__(type): def __init__(cls, name, bases, dct): def make_proxy(name): def proxy(self, *args): return getattr(self._obj, name) return proxy type.__init__(cls, name, bases, dct) if cls.__wraps__: ignore = set("__%s__" % n for n in cls.__ignore__.split()) for name in dir(cls.__wraps__): if name.startswith("__"): if name not in ignore and name not in dct: setattr(cls, name, property(make_proxy(name)))
用法:
class DictWrapper(Wrapper): __wraps__ = dict wrapped_dict = DictWrapper(dict(a=1, b=2, c=3)) # make sure it worked.... assert "b" in wrapped_dict # __contains__ assert wrapped_dict == dict(a=1, b=2, c=3) # __eq__ assert "'a': 1" in str(wrapped_dict) # __str__ assert wrapped_dict.__doc__.startswith("dict()") # __doc__
使用__getattr__
和__getattribute__
是类获取属性的最后一个资源。
考虑以下几点:
>>> class C: x = 1 def __init__(self): self.y = 2 def __getattr__(self, attr): print(attr) >>> c = C() >>> cx 1 >>> cy 2 >>> cz z
只有当__getattr__
方法没有其他的作用时才被调用(它不会在运算符上工作,你可以在这里阅读)。
在你的例子中, __repr__
和许多其他魔术方法已经在object
类中定义了。
有一件事是可以做到的,思想,就是定义这些魔术方法,然后调用__getattr__
方法。 通过我和它的答案检查这个问题( 链接 )看到一些代码这样做。
对于__getattr__,newstyle vs oldstyle类 (也可参见Python文档 )的不对称行为的答案,使用new样式类修改对__getattr__
或__getattribute__
“magic”方法的访问是不可能的。 这个限制使解释器更快。
从文档中剪切并复制:
对于旧式类,总是以与其他方法或属性完全相同的方式查找特殊方法。
对于新风格的类,只有在对象types定义的情况下,才能保证特殊方法的隐式调用,而不是在对象的实例字典中。