装饰器仅在装饰方法时有效,但在使用作为参数传递的方法调用装饰器时无效
Posted
技术标签:
【中文标题】装饰器仅在装饰方法时有效,但在使用作为参数传递的方法调用装饰器时无效【英文标题】:Decorator only works when method is decorated but not when the decorator is called with method passed as argument 【发布时间】:2022-01-19 20:12:40 【问题描述】:长期聆听者;第一次来电,所以如果我没有完全遵守指南,请不要拍我。
所以我正在尝试创建一个装饰器,它将状态栏应用于生成器方法。使用 @decorator 语法时,我的装饰器工作得很好。然而,当调用装饰器并将方法作为参数传递时,装饰器什么也不做。我想像调用任何其他函数一样调用装饰器函数,而不是使用@status_bar,因为我想要一种简单的方法来选择使用生成器。
这是工作代码:
import time
from alive_progress import alive_bar
def status_bar(func):
def wrapper(self):
with alive_bar() as bar:
for x in func(self):
bar()
return wrapper
class TestClass:
def __init__(self):
pass
@status_bar
def iter_range(self):
for x in range(10000):
time.sleep(0.01)
yield x
TestClass().iter_range()
这会按预期在提示中产生一个状态栏。
| ▶▶▶▶▶▶▶▶▶▶▶▶▶ ▂▂▄ 326 in 3s (98.2/s)
但是在没有语法糖的情况下运行代码什么都做不了。
def status_bar(func):
def wrapper(self):
with alive_bar() as bar:
for x in func(self):
bar()
return wrapper
class TestClass:
def __init__(self):
pass
def iter_range(self):
for x in range(10000):
time.sleep(0.01)
yield x
status_bar(TestClass().iter_range)
相反,代码就像没有调用任何函数一样运行。
进程以退出代码 0 结束
我似乎总是得到相同的输出,而且我知道这里有一个简单的解释,这就是为什么它让我如此烦恼以至于我无法让它工作。任何帮助是极大的赞赏。谢谢。
【问题讨论】:
【参考方案1】:我正在尝试自己学习这一点。使用https://***.com/a/25827070/59867 作为参考,我发现:
当您调用 status_bar(TestClass().iter_range)
时,它会返回您实际上并没有调用的修饰函数,这就是为什么没有发生任何事情的原因。
所以类似地调用它,但只是从类(而不是从实例)中传入函数,这样就可以了:
decorated_func = status_bar(TestClass.iter_range)
decorated_func(TestClass())
这意味着你会这样称呼它:
status_bar(TestClass.iter_range)(TestClass())
【讨论】:
我觉得很愚蠢,因为我不理解第二个括号在我见过的其他示例中是如何使用的。这似乎违反直觉,但我假设它与装饰器内的嵌套函数有关。知道为什么要使用这种语法吗?我想我不完全理解为什么调用装饰器的替代方法不起作用。 当您执行status_bar(TestClass().iter_range)
时,您调用的是status_bar()
,它只做一件事:构建并返回一个可以调用的函数。但是你没有调用它,所以什么都没有发生。如果您将()
添加到末尾,那么您将调用它返回给您的函数。
哦,好吧,我明白你的意思了。 @status_bar 有效地调用了自动返回的函数。但是对于典型的语法,我只是调用函数而不调用包装器,所以必须随后调用它?
正确 - 装饰器自动将包装后的函数分配给原始函数名,但直接调用时需要调用返回的函数以上是关于装饰器仅在装饰方法时有效,但在使用作为参数传递的方法调用装饰器时无效的主要内容,如果未能解决你的问题,请参考以下文章
AsyncTask(update) 仅在应用程序第一次打开时有效,但在 5 秒后不更新
Alamofire completionHandler 更新 ui 仅在函数完成时有效