python decorator代理不起作用

时间:2014-12-13 02:44:49

标签: python decorator

我尝试创建跟踪装饰器以跟踪调用哪个方法。 让我们先阅读代码。代码是用python 2.7编写的

class Decor(object):
    def __init__(self,cls):
        self.cls=cls

    def __call__(self,*arg):
        self.instance = self.cls(*arg)
        return self

    def __getattr__(self,name):
        print "attribute name of instance of class {}  = {}".format(self.cls.__name__,name)
        return getattr(self.instance,name)

@Decor
class Foo(object):
    attr1 = 10
    def __init__(self,value):
        self.value = value

    def method1(self):
        self.method2()
        print self
        print "value={}".format(self.value)

    def method2(self):
        print "hi"


f=Foo(10)
print f
f.method1()

输出:

1. <__main__.Decor object at 0x02758B90>
2. attribute name of instance of class Foo  = method1
3. hi
4. <__main__.Foo object at 0x02758C30>
5. value=10

我预计method2会以method1的形式获得print。你能解释为什么method2没有详细打印吗?

我不理解输出line4。它显示Foo对象而不是Decor对象。 f是一个Decor对象,为什么self是Foo对象?请解释一下!

请给我一个解决此问题的建议。感谢

2 个答案:

答案 0 :(得分:2)

此处的问题是self内的Foo.instance1Foo个实例,而不是Decor个实例。

如果您打印f.method1本身就可以看到这一点:它是一个实例为f的绑定方法:

>>> f.method1
attribute name of instance of class Foo  = method1
<bound method Foo.method1 of <__main__.Foo object at 0x10c641e90>>

为什么呢?好吧,你返回了getattr(self.instance, 'method1'),它与self.instance.method1做了同样的事情,所以它的self.instance会被绑定到绑定的方法中。

因此,当method1代码查找self.method2(或self.value)时,它会在Foo实例上查找,因此您的代码不会#39; t跑步。

如果您不理解这一点,请尝试阅读How Methods Work。 (如果您不想接受属性查找是不可思议的,请点击Descriptor HowTo Guide末尾的链接。)


因此,如果您希望self成为Decor内的instance1实例,则必须使用Decor实例返回绑定方法,而不是{{1实例。您可以通过types.MethodType手动创建一个来完成此操作。 (或者,一旦理解了描述符,就可以直接查找函数实现,然后手动调用它Foo

例如:

__get__

(如果你想让它与classmethods,staticmethods,非方法callables一起使用,比如存储在实例dict中的函数等等,当然它需要比def __getattr__(self, name): print "attribute name of instance of class {} = {}".format(self.cls.__name__,name) attr = getattr(self.instance, name) if callable(attr): return types.MethodType(attr.__func__, self, type(self)) else: return attr 更复杂一点。)

现在:

callable

所以,当我们称之为:

>>> f.method1
attribute name of instance of class Foo  = method1
Out[26]: <bound method Decor.method1 of <__main__.Decor object at 0x10c7e9f10>>

答案 1 :(得分:1)

My other answer解释了为什么您现有的代码无法正常工作以及如何修复它(虽然我不确定 如何解释它......)。< / p>

但是,通常还有一种更简单的方法可以做你正在尝试做的事情:你可以对这个类进行monkeypatch而不是包装它。当然,这意味着您必须使用__getattribute__而不是__getattr__,并且会不小心递归地给自己打电话:*

def Decor(cls):
    old_getattribute = getattr(cls, '__getattribute__')
    def new_getattribute(self, name):
        print "attribute name of instance of class {}  = {}".format(cls.__name__, name)
        return old_getattribute(self, name)
    cls.__getattribute__ = new_getattribute
    return cls

这可能会打印出比您想要的更多 - 例如,在f = Foo(10)期间,它会显示正在查找的__class__属性(but not __init__ )。您当然可以过滤掉您不想记录的任何名称。

这也有一些限制,包装和委托不起作用。它不会在旧式课程上工作;你不能用它动态地包装内置或C扩展类;另一方面,它确实意味着Foo有自己的名称,文档字符串等,而不是Decor,而不必在functools.wraps上复制它们}式的。


*请注意,文档说您应该始终在__getattribute__内调用属性访问的基类版本,但在这种情况下,您要调用cls的原始版本版本 - 如果cls没有定义一个版本,它当然是基类版本。