装饰器停止自动运行

时间:2019-06-27 14:44:48

标签: python python-decorators

我正在尝试使用参数实现装饰器。

这是我的实际代码:

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)

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

1 个答案:

答案 0 :(得分:0)

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

这里是简化版本,仅显示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