装饰器仅在装饰方法时有效,但在使用作为参数传递的方法调用装饰器时无效

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 有效地调用了自动返回的函数。但是对于典型的语法,我只是调用函数而不调用包装器,所以必须随后调用它? 正确 - 装饰器自动将包装后的函数分配给原始函数名,但直接调用时需要调用返回的函数

以上是关于装饰器仅在装饰方法时有效,但在使用作为参数传递的方法调用装饰器时无效的主要内容,如果未能解决你的问题,请参考以下文章

在函数装饰器中调用 Python 实例方法

AsyncTask(update) 仅在应用程序第一次打开时有效,但在 5 秒后不更新

Alamofire completionHandler 更新 ui 仅在函数完成时有效

selectRowAtIndexPath 仅在 allowedMultipleSelection 为 YES 时有效

jQuery animate 参数仅在添加 0 时有效?

java装饰设计模式