【问题标题】:How to globally toggle the usage of a decorator?如何全局切换装饰器的使用?
【发布时间】:2017-07-21 11:49:48
【问题描述】:

我希望创建一个全局切换变量来根据通过命令行传递的参数来打开和关闭我的装饰。

在下面的情况下,我不需要在不需要时注释掉@time_md5_comparison,而是根据传递的参数进行全局切换。


main.py

from timing_decorator import time_md5_comparison

@time_md5_comparison
def md5_comparison(a, b):
    if a==b:
        return True
    else:
        return False

timing_decorator.py

def time_md5_comparison(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = function(*args, **kwargs)
        t2 = time.time()
        print(str(function.__name__) + "  " + str("%.6f "%(t2 - t1)))
        return result
    return wrapper

我可以在main.py 中创建一个变量,比如USE_DECORATOR = True,以便调用装饰器,如果稍后将其设置为False,则调用原始函数?

【问题讨论】:

  • 有什么问题?
  • 我可以创建一个变量吗,比如 main.py 中的 USE_DECORATOR = True 在这种情况下调用装饰器。如果 USE_DECORATOR = False,则调用原始函数。
  • 查看这个答案 - stackoverflow.com/a/28654546/4497519 然后使用命令行中的参数进行切换将是微不足道的。

标签: python python-3.x python-decorators


【解决方案1】:

装饰器会影响在执行它们之后的函数定义时生成的字节码(在调用它时编译的函数本身运行之前并与它分开)。因此,除了重新加载包含修饰函数的整个模块之外,唯一可行的方法可能是根据标志变量的 current 设置以不同方式执行包装函数。

注意,toggle 变量必须放在一个可变容器中——list——这样被修饰的函数将引用它的当前值,而不是最初修饰时的值。


main.py

from timing_decorator import time_md5_comparison

USE_DECORATOR = [False]

@time_md5_comparison(USE_DECORATOR)
def md5_comparison(a, b):
    if a==b:
        return True
    else:
        return False

md5_comparison(3, 4)  # prints nothing
USE_DECORATOR[0] = True
md5_comparison(5, 6)  # prints timing info

接受除单个函数以外的参数的装饰器本质上是装饰器工厂,它们必须创建并返回实际使用的装饰器。这就是为什么您的问题中的装饰器需要嵌套更深一层。


timing_decorator.py

from functools import wraps
import time

def time_md5_comparison(disabled):
    def decorator(function):
        @wraps(function)
        def wrapped(*args, **kwargs):
            if disabled[0]:
                result = function(*args, **kwargs)
            else:
                t1 = time.time()
                result = function(*args, **kwargs)
                t2 = time.time()
                print(str(function.__name__)+"  "+ str("%.6f " %(t2 - t1)))
            return result

        return wrapped

    return decorator

【讨论】:

  • 我仍然不确定为什么我们使用可变容器,而不仅仅是一个变量。让我试一试以获得更好的理解。
  • 也让我有点意外,但我相信这是因为像 TrueFalse 这样的内置布尔值是不可变的,字符串和整数也是如此,所以当你将它们传递给函数它们的值被传递,而不是对它们的引用,因为它们无法更改。 list 的内容可变的,因此它们通过引用传递。
【解决方案2】:

是的。您可以将值传入,或者简单地重置装饰器。

重置装饰器是这样的:

import timing_module

if NO_USE_DECORATOR:
    mydecorator = timing_module.empty_decorator
else:
    mydecorator = timing_module.time_md5_comparison


@mydecorator
def myfunc(args):
    pass

显然,您不必将其称为mydecorator。您可以重置 time_md5_comparison 名称,以指向您喜欢的任何内容。

将值作为参数传递会将逻辑放入装饰器中,这样更简洁:

#module: timing_module
def original_time_md5_comparison(fn):
    """Original code of your decorator here"""
    # ...
    pass

def no_decorator(fn):
    """Decorator no-op. Just use original function."""
    return fn

def time_md5_comparison(use_decorator = True):
    if use_decorator:
        return original_time_md5_comparison
    else:
        return no_decorator

【讨论】:

    猜你喜欢
    • 2013-01-16
    • 2018-05-01
    • 2019-04-25
    • 1970-01-01
    • 1970-01-01
    • 2020-06-20
    • 1970-01-01
    • 1970-01-01
    • 2016-09-11
    相关资源
    最近更新 更多