Python 装饰器计数函数调用
Python decorators count function call
我正在回忆一些我还没有得到的 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) 的调用而不是函数调用自身,为什么它真的有效?
What I don't get here is why do we increment the calls of the function wrapper (helper.calls += 1) instead of the function calls itself, and why does it actually working?
我想让它成为一个通用的装饰器。你可以这样做
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,这很好。
据我所知,它之所以有效,是因为它用装饰器中的 helper
函数替换了函数 succ
(每次装饰函数时都会重新定义),所以 succ = helper
和 succ.calls = helper.calls
。 (当然,名称助手只在装饰器的命名空间内定义)
这有意义吗?
据我了解(如果我错了请纠正我)你程序执行的顺序是:
- 注册
call_function
。
- 注册
succ
。
- 在注册
succ
函数解释器时找到装饰器,因此它会执行 call_function
.
- 你的函数 returns 一个对象,它是一个函数 (
helper
)。并添加到此对象字段 calls
.
- 现在您的函数
succ
已分配给 helper
。所以当你调用你的函数时,你实际上是在调用 helper
函数,包装在一个装饰器中。因此,您添加到辅助函数的每个字段都可以通过寻址 succ
在外部访问,因为这 2 个变量指的是同一事物。
- 所以当你调用
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.
关于装饰器要记住的重要一点是,装饰器是一个 函数 ,它将一个函数作为参数,return 又是另一个函数。 returned 值 - 另一个函数 - 是调用原始函数名称时将调用的值。
这个模型可以很简单:
def my_decorator(fn):
print("Decorator was called")
return fn
在这种情况下,returned 函数与传入函数相同。但这通常不是你要做的。通常,您 return 一个完全不同的函数,或者您 return 一个以某种方式链接或包装原始函数的函数。
在您的示例中,这是一个非常常见的模型,您有一个内部函数 returned:
def helper(x):
helper.calls += 1
return func(x)
此内部函数调用原始函数 (return func(x)
),但它也会增加调用计数器。
这个内部函数作为 "replacement" 被插入,用于装饰的任何函数。因此,当您的模块 foo.succ()
函数被查找时,结果是对由装饰器编辑的内部辅助函数 return 的引用。该函数递增调用计数器,然后调用最初定义的 succ
函数。
装饰函数时,您“替换”了包装器的函数。
在这个例子中,装饰后,当你调用succ
时,你实际上是在调用helper
。因此,如果您正在计算通话次数,则必须增加 helper
次通话。
您可以通过检查修饰函数的属性 _name_ 来检查装饰函数后名称是否与包装器绑定:
def call_counter(func):
def helper(*args, **kwargs):
helper.calls += 1
print(helper.calls)
return func(*args, **kwargs)
helper.calls = 0
return helper
@call_counter
def succ(x):
return x + 1
succ(0)
>>> 1
succ(1)
>>> 2
print(succ.__name__)
>>> 'helper'
print(succ.calls)
>>> 2
带有 Class 装饰器的示例
当您使用 Class 装饰器 装饰一个函数时,每个函数都有自己的 call_count。这是 OOP 的简单性。每次调用CallCountDecorator对象都会增加自己的call_count属性并打印它。
class CallCountDecorator:
"""
A decorator that will count and print how many times the decorated function was called
"""
def __init__(self, inline_func):
self.call_count = 0
self.inline_func = inline_func
def __call__(self, *args, **kwargs):
self.call_count += 1
self._print_call_count()
return self.inline_func(*args, **kwargs)
def _print_call_count(self):
print(f"The {self.inline_func.__name__} called {self.call_count} times")
@CallCountDecorator
def function():
pass
@CallCountDecorator
def function2(a, b):
pass
if __name__ == "__main__":
function()
function2(1, b=2)
function()
function2(a=2, b=3)
function2(0, 1)
# OUTPUT
# --------------
# The function called 1 times
# The function2 called 1 times
# The function called 2 times
# The function2 called 2 times
# The function2 called 3 times
我正在回忆一些我还没有得到的 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) 的调用而不是函数调用自身,为什么它真的有效?
What I don't get here is why do we increment the calls of the function wrapper (helper.calls += 1) instead of the function calls itself, and why does it actually working?
我想让它成为一个通用的装饰器。你可以这样做
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,这很好。
据我所知,它之所以有效,是因为它用装饰器中的 helper
函数替换了函数 succ
(每次装饰函数时都会重新定义),所以 succ = helper
和 succ.calls = helper.calls
。 (当然,名称助手只在装饰器的命名空间内定义)
这有意义吗?
据我了解(如果我错了请纠正我)你程序执行的顺序是:
- 注册
call_function
。 - 注册
succ
。 - 在注册
succ
函数解释器时找到装饰器,因此它会执行call_function
. - 你的函数 returns 一个对象,它是一个函数 (
helper
)。并添加到此对象字段calls
. - 现在您的函数
succ
已分配给helper
。所以当你调用你的函数时,你实际上是在调用helper
函数,包装在一个装饰器中。因此,您添加到辅助函数的每个字段都可以通过寻址succ
在外部访问,因为这 2 个变量指的是同一事物。 - 所以当你调用
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.
关于装饰器要记住的重要一点是,装饰器是一个 函数 ,它将一个函数作为参数,return 又是另一个函数。 returned 值 - 另一个函数 - 是调用原始函数名称时将调用的值。
这个模型可以很简单:
def my_decorator(fn):
print("Decorator was called")
return fn
在这种情况下,returned 函数与传入函数相同。但这通常不是你要做的。通常,您 return 一个完全不同的函数,或者您 return 一个以某种方式链接或包装原始函数的函数。
在您的示例中,这是一个非常常见的模型,您有一个内部函数 returned:
def helper(x):
helper.calls += 1
return func(x)
此内部函数调用原始函数 (return func(x)
),但它也会增加调用计数器。
这个内部函数作为 "replacement" 被插入,用于装饰的任何函数。因此,当您的模块 foo.succ()
函数被查找时,结果是对由装饰器编辑的内部辅助函数 return 的引用。该函数递增调用计数器,然后调用最初定义的 succ
函数。
装饰函数时,您“替换”了包装器的函数。
在这个例子中,装饰后,当你调用succ
时,你实际上是在调用helper
。因此,如果您正在计算通话次数,则必须增加 helper
次通话。
您可以通过检查修饰函数的属性 _name_ 来检查装饰函数后名称是否与包装器绑定:
def call_counter(func):
def helper(*args, **kwargs):
helper.calls += 1
print(helper.calls)
return func(*args, **kwargs)
helper.calls = 0
return helper
@call_counter
def succ(x):
return x + 1
succ(0)
>>> 1
succ(1)
>>> 2
print(succ.__name__)
>>> 'helper'
print(succ.calls)
>>> 2
带有 Class 装饰器的示例
当您使用 Class 装饰器 装饰一个函数时,每个函数都有自己的 call_count。这是 OOP 的简单性。每次调用CallCountDecorator对象都会增加自己的call_count属性并打印它。
class CallCountDecorator:
"""
A decorator that will count and print how many times the decorated function was called
"""
def __init__(self, inline_func):
self.call_count = 0
self.inline_func = inline_func
def __call__(self, *args, **kwargs):
self.call_count += 1
self._print_call_count()
return self.inline_func(*args, **kwargs)
def _print_call_count(self):
print(f"The {self.inline_func.__name__} called {self.call_count} times")
@CallCountDecorator
def function():
pass
@CallCountDecorator
def function2(a, b):
pass
if __name__ == "__main__":
function()
function2(1, b=2)
function()
function2(a=2, b=3)
function2(0, 1)
# OUTPUT
# --------------
# The function called 1 times
# The function2 called 1 times
# The function called 2 times
# The function2 called 2 times
# The function2 called 3 times