我正在刷新一些我尚未得到的python功能,我正在学习this python tutorial并且有一个我不完全理解的例子。它是关于计算函数调用的装饰器,这里是代码:
def call_counter(func):
def helper(x):
helper.calls += 1
return func(x)
helper.calls = 0
return helper
@call_counter
def succ(x):
return x + 1
if __name__ == '__main__':
print(succ.calls)
for i in range(10):
print(succ(i))
print(succ.calls)
我没有得到的是为什么我们增加函数包装器的调用(helper.calls + = 1)而不是函数调用本身,为什么它实际上工作?
答案 0 :(得分:4)
要记住关于装饰器的重要一点是装饰器是一个函数,它将一个函数作为参数,并返回另一个函数。返回的值 - 另一个函数 - 是在调用原始函数的名称时调用的。
这个模型非常简单:
def my_decorator(fn):
print("Decorator was called")
return fn
在这种情况下,返回的函数与传入函数相同。但这通常不是你做的。通常,您返回一个完全不同的函数,或者返回一个以某种方式链接或包装原始函数的函数。
在您的示例中,这是一个非常常见的模型,您将返回一个内部函数:
def helper(x):
helper.calls += 1
return func(x)
此内部函数调用原始函数(return func(x)
),但它也会增加调用计数器。
这个内部功能正被插入作为替换"无论正在装饰什么功能。因此,当查找模块foo.succ()
函数时,结果是对装饰器返回的内部辅助函数的引用。该函数递增调用计数器,然后调用最初定义的succ
函数。
答案 1 :(得分:1)
我不能在这里得到的是为什么我们增加函数包装器的调用(helper.calls + = 1)而不是函数调用本身,为什么它实际上有效?
我认为要使它成为一个非常有用的装饰者。你可以这样做
def succ(x):
succ.calls += 1
return x + 1
if __name__ == '__main__':
succ.calls = 0
print(succ.calls)
for i in range(10):
print(succ(i))
print(succ.calls)
工作正常,但你需要将.calls +=1
放在你想要应用它的每个函数中,并在运行任何函数之前初始化为0。如果你想要计算一大堆功能,那肯定会更好。另外,它在定义时将它们初始化为0,这很不错。
据我了解它可行,因为它用装饰器中的succ
函数替换了函数helper
(每次装饰函数时都重新定义)所以succ = helper
和{ {1}}。 (当然,名称助手只在装饰器的命名空间内定义)
这有意义吗?
答案 2 :(得分:1)
据我所知(如果我错了,请纠正我)你编程执行的命令是:
call_function
。succ
。 succ
函数解释器时会找到一个装饰器,因此它会执行call_function
。helper
)。并添加到此对象字段calls
。succ
已分配到helper
。因此,当你调用你的函数时,你实际上正在调用helper
函数,包装在装饰器中。因此,通过寻址succ
可以在外部访问您添加到辅助函数的每个字段,因为这两个变量引用相同的内容。 succ()
时,如果你做helper(*args, **argv)
检查出来:
def helper(x):
helper.calls += 1
return 2
helper.calls = 0
def call_counter(func):
return helper
@call_counter
def succ(x):
return x + 1
if __name__ == '__main__':
print(succ == helper) # prints true.
答案 3 :(得分:1)
当你装饰一个功能时,你可以替换"你可以使用包装器。
在此示例中,在装饰后,当您致电succ
时,您实际上正在呼叫helper
。因此,如果您正在计算呼叫,则必须增加helper
次呼叫。
您可以通过检查已修饰函数的属性__name__来检查一旦您修饰函数,该名称与包装器绑定在一起:
def call_counter(func):
def helper(x):
helper.calls += 1
return func(x)
helper.calls = 0
return helper
@call_counter
def succ(x):
return x + 1
print(succ.__name__)
>>> 'helper'