我正在尝试使用参数实现装饰器。
这是我的实际代码:
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)
这有可能吗?我只能实现没有参数的包装器或带有参数的自动装饰器。
答案 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