Python 装饰器 - 类中的 __call__
Posted
技术标签:
【中文标题】Python 装饰器 - 类中的 __call__【英文标题】:Python Decorators - __call__ in class 【发布时间】:2013-10-30 03:17:20 【问题描述】:我试图理解 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)
替换。
你能帮我么?没有装饰器的等效程序如何?
谢谢!
【问题讨论】:
【参考方案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()
【讨论】:
【参考方案2】:这里有一个修复:
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
【讨论】:
接受函数的参数使用 def __call__(self, *args,**kwargs): 并将它们传递给 f【参考方案3】:afunction
正在被myDecorator
类的实例替换。该类有一个__call__
方法,因此可以像函数一样调用实例。因此,假设它的签名是兼容的(通常装饰器返回使用*args
和**kwargs
的东西),该实例可以用作原始函数的替换。
通常__call__
方法会调用包装函数。在__init__
中这样做通常是不正确的; __init__
应该将包装函数的引用存储为实例属性,以便 __call__
可以调用它。
【讨论】:
【参考方案4】:你的代码的输出是
inside myDecorator.__init__()
inside aFunction()
Finished decorating aFunction()
inside myDecorator.__call__()
首先,你知道这个@装饰器语法是什么意思吗?
@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 实例时,使其调用myDecorator 的__call__
方法,该方法执行。请注意,f()
在这种情况下与f.__call__(f)
的含义相同,因为__call__
方法用于启用和覆盖默认对象的调用行为(简而言之,当定义了__call__
方法时,任何对象都是可调用的)。
如果你想在一个函数被调用的时候执行它,那么你应该把它赋值给__init__
中的实例变量,然后在myDecorator的__call__
中调用它。
【讨论】:
【参考方案5】:这就是装饰器的全部目的:用装饰器返回的函数替换(或者,更常见的是包装)一个函数。在您的情况下,aFunction
将被替换为 myDecorator
的实例,因此当您调用 aFunction()
时,您实际上是在调用该实例:在 Python 中,调用类实例会调用其 __call__
方法。
定义一个装饰函数完全等价于:
def aFunction():
print("inside aFunction()")
aFunction = myDecorator(aFunction)
当然,包装函数通常会在执行完任何操作后调用原始函数。
【讨论】:
以上是关于Python 装饰器 - 类中的 __call__的主要内容,如果未能解决你的问题,请参考以下文章