【问题标题】:Decorator Stop AutoRun装饰器停止自动运行
【发布时间】:2019-11-09 15:00:15
【问题描述】:

我正在尝试实现一个带参数的装饰器。

这是我的实际代码:

def transactional_function(read_only=False):
    """
    A simple wrapper to ensure that the desired function will always runs
    inside a transaction, so we don't have to pollute our code with a bunch of
    run_as_transactions.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            in_transaction = getattr(_thread_local_data, "is_in_transaction", False)

            if in_transaction:
                result = func(*args, **kw)
            else:
                if read_only:
                    return run_as_readonly(func, *args, **kw)
                else:
                    return run_as_transaction(func, *args, **kw)
            return result
        return wrapper
    return decorator

但是,这会在函数声明时自动运行。

所以理论上我只需要一个包装器,但我希望我可以为装饰函数添加参数。

decorated_function(standard_arg, read_only=True)

这有可能吗?我只能实现不带参数的包装器或带参数自动运行的装饰器。

【问题讨论】:

  • 您发布的内容不会自动运行装饰功能;让我们看一个实际使用这个装饰器的例子,也许那里有问题。包装器可以接受任何你想要的参数,它没有义务将它们传递给被包装的函数。
  • 正如@jasonharper 指出的那样,您的问题的代码不会运行被修饰的函数——所以其他原因导致了这种情况的发生。请edit您的问题并提供minimal reproducible example说明问题。

标签: python python-decorators


【解决方案1】:

这是可能的,但是你开始的方式不对:你想要的不是向装饰器添加参数,而是向被装饰的函数添加参数。在一般情况下这是相当棘手的,因为您必须将该参数添加到修饰函数并在调用原始函数之前将其删除,除非它已经存在于原始函数中。

这里是一个简化版,只显示read_only状态,调用原函数:

import inspect
from functools import wraps

def transactional_function(func):
    sig = inspect.signature(func)   # extract the original signature
    params = sig.parameters
    if 'read_only' in params:
        change = False              # read_only already exists, just note that
    else:
        change = True
        # let us add a new keyword only parameter 'read_only'
        params = list(params.values())
        params.append(inspect.Parameter('read_only',
                        inspect.Parameter.KEYWORD_ONLY))
        # re-order the parameter list
        params.sort(key=lambda x: {
            inspect.Parameter.POSITIONAL_OR_KEYWORD: 0,
            inspect.Parameter.POSITIONAL_ONLY: 0,
            inspect.Parameter.VAR_POSITIONAL:1,
            inspect.Parameter.KEYWORD_ONLY:2,
            inspect.Parameter.VAR_KEYWORD:3}[x.kind])
        # and set that new parameter list in the signature
        sig = sig.replace(parameters = params)
    @wraps(func)
    def inner(*args, **kwargs):
        read_only = kwargs.get('read_only', False)
        # remove the parameter if it was added by the decorator
        if change and ('read_only' in kwargs):
            del kwargs['read_only']
        print('read_only', read_only)       # add your logic here
        return func(*args, **kwargs)
    # update the signature of the decorated function
    if change:
        inner.__signature__ = sig
    return inner

演示:

>>> @transactional_function
def foo(bar):
    return bar * 2

>>> foo(2)
read_only False
4
>>> foo(2, read_only=False)
read_only False
4
>>> foo(2, read_only=True)
read_only True
4

还有更复杂的签名:

>>> @transactional_function
def foo2(a, *args, **kwargs):
    return a
>>> foo2(2, read_only=True)
read_only True
2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-20
    • 2012-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-08
    • 1970-01-01
    • 2019-04-25
    相关资源
    最近更新 更多