python装饰器代理

2024-06-16 14:19:06 发布

您现在位置:Python中文网/ 问答频道 /正文

我尝试创建一个跟踪装饰器来跟踪哪个方法得到了调用。 让我们先读代码。代码是用python2.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()

输出:

^{pr2}$

我以为method2会被打印成method1。你能解释一下为什么method2没有打印出来吗?在

我不明白输出线4。它显示的是Foo对象而不是Decor对象。f是装饰对象,为什么self是Foo对象?请解释一下!在

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


Tags: 对象instance代码nameselffoovaluedef
2条回答

My other answer解释了为什么现有代码不能工作,以及如何修复它(尽管我不确定它是如何解释的)。在

但是通常还有一种更简单的方法来完成您要做的事情:您可以对类进行monkeypatch,而不是包装它。当然,这意味着您必须使用^{}而不是^{},并附带所有有关意外递归调用自己的常见警告:*

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 ^{})。当然,你可以过滤掉任何你不想记录的名字。在

这也有一些包装和委托没有的限制。它不能在旧样式的类上工作;你不能用它来动态包装内置或C扩展类;等等。另一方面,它确实意味着Foo有自己的名称,docstring等,而不是Decor,而不是{},而不是{}-样式。在


*请注意,文档中说您应该始终调用基类版本来访问__getattribute__中的属性,但是在这种情况下,您希望调用{}的原始版本,如果{}没有定义基类版本,那么它当然是基类版本。

这里的问题是Foo.instance1内的selfFoo实例,而不是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相同,所以{}被烘焙到绑定方法中。在

因此,当method1代码查找self.method2(或self.value)时,它是在Foo实例上查找它,因此您的代码不会运行。在

如果您不明白,请尝试阅读How Methods Work。(如果您不想仅仅接受属性查找的魔力,请按照末尾的链接Descriptor HowTo Guide。)


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

例如:

^{pr2}$

(如果您想让它与classmethods、staticmethods、实例dict中存储的函数等非方法可调用项一起工作,那么它当然需要比callable更复杂的一点。)

现在:

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

所以,当我们称之为:

>>> f.method1()
attribute name of instance of class Foo  = method1
attribute name of instance of class Foo  = method2
hi
<__main__.Decor object at 0x10c7e9f10>
attribute name of instance of class Foo  = value
value=10

相关问题 更多 >