如何在python中使用条件装饰器?

Posted

技术标签:

【中文标题】如何在python中使用条件装饰器?【英文标题】:How to use a conditional decorator in python? 【发布时间】:2019-03-03 09:18:14 【问题描述】:

在尝试理解 python 中的条件装饰器时,我遇到了this example。该问题的公认答案解释了如何定义条件装饰器,而不是如何使用它。

示例代码如下:

class conditional_decorator(object):
    def __init__(self, dec, condition):
        self.decorator = dec
        self.condition = condition

    def __call__(self, func):
        if not self.condition:
            # Return the function unchanged, not decorated.
            return func
        return self.decorator(func)

@conditional_decorator(timeit, doing_performance_analysis)
def foo():
    time.sleep(2) 

但是如何使用它呢?我尝试了以下foo 的调用,如下所示:

doing_performance_analysis=False
foo()

doing_performance_analysis=True
foo()

但我收到以下错误:

Traceback (most recent call last):
  File "tester.py", line 18, in <module>
    @conditional_decorator(timeit, doing_performance_analysis)
NameError: name 'doing_performance_analysis' is not defined

那么它是如何正常工作的呢?

【问题讨论】:

正如 Martijn 在他的 cmets 中所说的那样,这只是有条件的,因为它是在 import 时间进行评估的。您不能在调用时更改它。 你得到的错误告诉你你没有设置名字doing_performance_analysis。您是否尝试过将该名称定义为布尔值? 我想我已经将该名称设置为布尔值,请参阅我的问题。我不确定你的意思是什么...... 您需要在应用装饰器时设置它。这就是为什么@conditional_decorator() 行出现错误的原因。 所以当我需要在开头设置名称时,我根本没有条件装饰器,而是静态的,不可更改的装饰器...... 【参考方案1】:

您可以将 condition 改为一个函数,并让装饰器返回一个函数包装器,以便它在运行时评估您想要的设置变量。

from functools import wraps
class conditional_decorator(object):
    def __init__(self, dec, predicate):
        self.decorator = dec
        self.predicate = predicate

    def __call__(self, func):
        decorated_func = self.decorator(func)
        @wraps(func)
        def wrapper(*args, **kwargs):
            if self.predicate():
                return decorated_func(*args, **kwargs)
            return func(*args, **kwargs)
        return wrapper

@conditional_decorator(timeit, lambda: doing_performance_analysis)
def foo():
    time.sleep(2)

这样就可以按您的意愿工作:

doing_performance_analysis=False
foo()

doing_performance_analysis=True
foo()

【讨论】:

对我来说似乎不对......它在定义 foo 时评估条件,而不是在您评估 foo() 时评估条件 确实如此。现在修好了。谢谢。 我很想只装饰一次函数,每次调用都装饰它似乎有点浪费【参考方案2】:

如果你使用 python 3 wrapt 模块,你可以设置一个启用标志来打开或关闭你的装饰器。出于调试原因,我尝试将给定函数的特定参数粘贴到剪贴板(使用熊猫)。这是由以下装饰器完成的。

CLIPTRACE = True

def traceClipboard(fieldname):
    """ give fieldname of the functions formal parameter 
        to get value dumped to clipboard
        also use wrapt parameter to disable if cliptrace is not set
    """
    @wrapt.decorator(enabled=CLIPTRACE)
    def wrapper(wrapped, instance, args, kwargs):
        args_list = inspect.getfullargspec(wrapped)[0]
        if "self" in args_list:
            args_list.pop(0)
        if fieldname in args_list:
            pyperclip.copy(args[args_list.index(fieldname)])

        if fieldname in kwargs.keys():
            pyperclip.copy(kwargs.get(fieldname))
        return wrapped(*args, **kwargs)
    return wrapper

然后用它来装饰我的会话类的功能:

 @traceClipboard("url")
 def _get(self, url, odata=None):
    """single _get request..."""

只要 CLIPTRACE 为 True,参数“url”的值就会被复制到剪贴板,在生产环境中 CLIPTRACE 为 False,不会执行剪贴板复制。

【讨论】:

以上是关于如何在python中使用条件装饰器?的主要内容,如果未能解决你的问题,请参考以下文章

Python闭包和装饰器

python笔记-4(装饰器生成器迭代器)

python语言中的装饰器详解

python测试开发(02-闭包函数+装饰器)

如何在python中使用**kwargs作为装饰器的参数

Python闭包和装饰器