10

我试图理解 Python 装饰器,并试图编写一个与此等效的程序:

class myDecorator(object):
   def __init__(self, f):
      print ("inside myDecorator.__init__()")
      f() # Prove that function definition has completed

   def __call__(self):
      print ("inside myDecorator.__call__()")

@myDecorator
def aFunction():
   print ("inside aFunction()")

print ("Finished decorating aFunction()")

aFunction()

问题是我不明白__call__类的方法是如何通过调用来调用aFunction()的。

正在aFunction()被替换myDecorator.__call__(aFunction)。你能帮我么?没有装饰器的等效程序如何?

谢谢!

4

5 回答 5

9

您的代码的输出是

inside myDecorator.__init__()
inside aFunction()
Finished decorating aFunction()
inside myDecorator.__call__()

首先,你知道这个@decorator 语法是什么意思吗?

@decorator
def function(a):
    pass

只是另一种说法:

def function(a):
    pass
function = decorator(function)

所以,在你的情况下

@myDecorator
def aFunction():
   print ("inside aFunction()")

只是意味着

def aFunction():
    print ("inside aFunction()")
aFunction = myDecorator(aFunction)

首先,您基本上创建了一个 myDecorator 类的新实例,调用它的构造函数 (__init__) 并将 aFunction 函数对象传递给它。然后,它执行打印和给定的功能。另外,请注意,这是在解释器加载函数时发生的,而不是在执行时发生的,因此如果您从该文件中导入某些内容,它将在那时执行,而不是在使用或调用时执行。

然后,执行aFunction(),当 aFunction 仍然引用 myDecorator 实例时,使其调用__call__myDecorator 的方法,该方法执行。请注意,这与本例中f()的含义相同,因为方法用于启用和覆盖默认对象的调用行为(简而言之,任何对象在定义了方法时都是可调用的)。f.__call__(f)__call____call__

如果要在调用时执行 aFunction,则应将其分配给实例变量 in并在myDecorator中__init__调用它。__call__

于 2013-10-21T18:17:45.850 回答
2

这就是装饰器的全部目的:用装饰器返回的函数替换(或者更常见的是包装)一个函数。在您的情况下,aFunction被替换为 的实例myDecorator,因此当您调用时,aFunction()您实际上是在调用该实例:在 Python 中,调用类实例会调用其__call__方法。

定义一个装饰函数完全等价于:

def aFunction():
    print("inside aFunction()")
aFunction = myDecorator(aFunction)

当然,通常包装函数会在完成任何操作后调用原始函数。

于 2013-10-21T14:55:18.880 回答
1

afunction正在被类的一个实例所取代myDecorator。类有一个__call__方法,所以实例可以像函数一样被调用。因此,假设它的签名是兼容的(通常装饰器返回使用*argsand的东西**kwargs),该实例可以用作原始函数的替换。

通常,该__call__方法将调用包装函数。这样做__init__通常是不正确的。__init__应该将包装函数的引用存储为实例属性,以便__call__可以调用它。

于 2013-10-21T14:54:53.530 回答
1

你好正确的方法是这样你有一个元类> --- Decoratorclass > --> 实际类

    class Meta(type):
        """ Metaclass  """

        def __call__(cls, *args, **kwargs):
            instance = super(Meta, cls).__call__(*args, **kwargs)
            return instance

        def __init__(cls, name, base, attr):
            super(Meta, cls).__init__(name, base, attr)


    class counted(metaclass=Meta):

        """ counts how often a function is called """

        def __init__(self, func):
            self.func = func

        def __call__(self, *args, **kwargs):
            print("Timer Start ")
            Tem = self.func(*args, **kwargs)
            print("Timer End {} ".format(Tem))
            return Tem


    class Test(object):

        def __init__(self, *args, **kwargs):
            pass

        @counted
        def methodA():
            print("Method A")
            return "1111"


    if __name__ == "__main__":
        obj = Test()
        obj.methodA()
于 2019-10-04T20:15:38.027 回答
0

这是一个修复:

class myDecorator(object):
   def __init__(self, f):
      print ("inside myDecorator.__init__()")
      self.f = f #Store the function being wrapped

   def __call__(self):
      print ("inside myDecorator.__call__()")
      self.f() # Call the wrapped function
于 2013-10-21T17:24:23.510 回答