我有一个由装饰器转移变量insurance_mode的问题。我将通过以下装饰器语句来实现:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
但不幸的是,这种说法并不管用。也许也许有更好的办法来解决这个问题。
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
下面是一个使用带有参数的装饰器的Flask示例。假设我们有一个路由'/user/name',我们想要映射到他的主页。
def matchR(dirPath):
def decorator(func):
def wrapper(msg):
if dirPath[0:6] == '/user/':
print(f"User route '{dirPath}' match, calling func {func}")
name = dirPath[6:]
return func(msg2=name, msg3=msg)
else:
print(f"Input dirPath '{dirPath}' does not match route '/user/'")
return
return wrapper
return decorator
#@matchR('/Morgan_Hills')
@matchR('/user/Morgan_Hills')
def home(**kwMsgs):
for arg in kwMsgs:
if arg == 'msg2':
print(f"In home({arg}): Hello {kwMsgs[arg]}, welcome home!")
if arg == 'msg3':
print(f"In home({arg}): {kwMsgs[arg]}")
home('This is your profile rendered as in index.html.')
输出:
User route '/user/Morgan_Hills' match, calling func <function home at 0x000001DD5FDCD310>
In home(msg2): Hello Morgan_Hills, welcome home!
In home(msg3): This is your profile rendered as in index.html.
假设你有一个函数
def f(*args):
print(*args)
你想要添加一个接受参数的装饰器,就像这样:
@decorator(msg='hello')
def f(*args):
print(*args)
这意味着Python将对f进行如下修改:
f = decorator(msg='hello')(f)
因此,部件装饰器(msg='hello')的返回值应该是一个包装器函数,它接受函数f并返回修改后的函数。然后可以执行修改后的函数。
def decorator(**kwargs):
def wrap(f):
def modified_f(*args):
print(kwargs['msg']) # use passed arguments to the decorator
return f(*args)
return modified_f
return wrap
所以,当你调用f时,就像你在做:
装饰(味精= '你好')(f) (args)
=== wrap(f)(args) === modified_f(args)
但是modified_f可以访问传递给装饰器的kwargs
的输出
f(1,2,3)
将会是:
hello
(1, 2, 3)
带参数的装饰器的语法有点不同——带参数的装饰器应该返回一个函数,该函数将接受一个函数并返回另一个函数。它应该返回一个普通的装饰器。有点困惑,对吧?我的意思是:
def decorator_factory(argument):
def decorator(function):
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
result = function(*args, **kwargs)
more_funny_stuff()
return result
return wrapper
return decorator
在这里你可以读到更多关于这个主题的内容——也可以使用可调用对象来实现这个功能,这里也有解释。
这是一个函数装饰器模板,如果没有参数,则不需要(),并且支持位置参数和关键字参数(但需要检查locals(),以确定第一个参数是否是要装饰的函数):
import functools
def decorator(x_or_func=None, *decorator_args, **decorator_kws):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kws):
if 'x_or_func' not in locals() \
or callable(x_or_func) \
or x_or_func is None:
x = ... # <-- default `x` value
else:
x = x_or_func
return func(*args, **kws)
return wrapper
return _decorator(x_or_func) if callable(x_or_func) else _decorator
下面是一个例子:
def multiplying(factor_or_func=None):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'factor_or_func' not in locals() \
or callable(factor_or_func) \
or factor_or_func is None:
factor = 1
else:
factor = factor_or_func
return factor * func(*args, **kwargs)
return wrapper
return _decorator(factor_or_func) if callable(factor_or_func) else _decorator
@multiplying
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying()
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying(10)
def summing(x): return sum(x)
print(summing(range(10)))
# 450
或者,如果不需要位置参数,可以不检查wrapper()中的第一个参数(从而不需要使用locals()):
import functools
def decorator(func_=None, **decorator_kws):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kws):
return func(*args, **kws)
return wrapper
if callable(func_):
return _decorator(func_)
elif func_ is None:
return _decorator
else:
raise RuntimeWarning("Positional arguments are not supported.")
下面是一个例子:
import functools
def multiplying(func_=None, factor=1):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return factor * func(*args, **kwargs)
return wrapper
if callable(func_):
return _decorator(func_)
elif func_ is None:
return _decorator
else:
raise RuntimeWarning("Positional arguments are not supported.")
@multiplying
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying()
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying(factor=10)
def summing(x): return sum(x)
print(summing(range(10)))
# 450
@multiplying(10)
def summing(x): return sum(x)
print(summing(range(10)))
# RuntimeWarning Traceback (most recent call last)
# ....
# RuntimeWarning: Positional arguments are not supported.
(部分改编自@ShitalShah的回答)
这是curry函数的一个很好的用例。
curry函数本质上是延迟函数的调用,直到提供了所有输入。
这可以用于各种事情,如包装器或函数式编程。在本例中,让我们创建一个接受输入的包装器。
我将使用一个简单的包pamda,其中包含一个用于python的curry函数。这可以用作其他函数的包装器。
安装 Pamda:
pip install pamda
创建一个简单的带有两个输入的装饰函数:
@pamda.curry()
def my_decorator(input, func):
print ("Executing Decorator")
print(f"input:{input}")
return func
使用提供给目标函数的第一个输入应用你的装饰器:
@my_decorator('Hi!')
def foo(input):
print('Executing Foo!')
print(f"input:{input}")
执行你的包装函数:
x=foo('Bye!')
把所有东西放在一起:
from pamda import pamda
@pamda.curry()
def my_decorator(input, func):
print ("Executing Decorator")
print(f"input:{input}")
return func
@my_decorator('Hi!')
def foo(input):
print('Executing Foo!')
print(f"input:{input}")
x=foo('Bye!')
将:
Executing Decorator
input:Hi!
Executing Foo!
input:Bye!
上面的回答很棒。这个例子还演示了@wraps,它从原始函数中获取文档字符串和函数名,并将其应用于新的包装版本:
from functools import wraps
def decorator_func_with_args(arg1, arg2):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
print("Before orginal function with decorator args:", arg1, arg2)
result = f(*args, **kwargs)
print("Ran after the orginal function")
return result
return wrapper
return decorator
@decorator_func_with_args("foo", "bar")
def hello(name):
"""A function which prints a greeting to the name provided.
"""
print('hello ', name)
return 42
print("Starting script..")
x = hello('Bob')
print("The value of x is:", x)
print("The wrapped functions docstring is:", hello.__doc__)
print("The wrapped functions name is:", hello.__name__)
打印:
Starting script..
Before orginal function with decorator args: foo bar
hello Bob
Ran after the orginal function
The value of x is: 42
The wrapped functions docstring is: A function which prints a greeting to the name provided.
The wrapped functions name is: hello