@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
#1 楼
带参数的装饰器的语法有些不同-带参数的装饰器应返回一个函数,该函数将接受一个函数并返回另一个函数。因此,它实际上应该返回一个普通的装饰器。有点混乱吧?我的意思是: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
在这里您可以阅读有关此主题的更多信息-也可以使用可调用对象来实现此目的,这在此处也有解释。 >
评论
我想知道为什么GVR没有通过在“功能”之后将参数作为后续装饰器参数传入来实现它。 “ Y,我听说你喜欢关闭……”等等。
–米歇尔·穆勒(MichelMüller)
14年4月8日在16:22
>函数是第一个参数还是最后一个参数?显然,首先,因为参数是可变长度的参数列表。 >奇怪的是,您用不同于定义的签名来“调用”该函数。正如您所指出的那样,它实际上非常合适-与类方法的调用方式非常相似。为了更加清楚,您可以使用decorator(self_func,param1,...)约定。但是请注意:我不主张在此进行任何更改,Python的实现还远远不够,我们可以看到突破性的更改是如何解决的。
–米歇尔·穆勒(MichelMüller)
2015年2月19日在1:07
您忘记了非常有用的functools.wraps装饰包装器:)
–套接字对
15年8月13日在21:19
您在调用函数时忘记了返回,即返回函数(* args,** kwargs)
–formiaczek
2015年12月1日17:09
也许很明显,但是以防万一:您需要将此装饰器用作@decorator(),而不仅是@decorator,即使您只有可选参数。
–帕特里克·梅夫克(Patrick Mevzek)
17年4月4日在20:25
#2 楼
编辑:要深入了解装饰者的心理模型,请看一下这个很棒的Pycon Talk。非常值得花30分钟。考虑带参数的装饰器的一种方法是
@decorator
def foo(*args, **kwargs):
pass
转换为
foo = decorator(foo)
因此,如果装饰器带有参数,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
转换为
foo = decorator_with_args(arg)(foo)
/>
decorator_with_args
是一个函数,它接受自定义参数并返回实际的装饰器(将应用于装饰的函数)。我使用带有局部变量的简单技巧使装饰器变得容易
from functools import partial
def _pseudo_decor(fun, argument):
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def foo(*args, **kwargs):
pass
更新:
上面,
foo
变成real_decorator(foo)
装饰函数的一个作用是名称
foo
是在装饰器声明上重写。 foo
被real_decorator
返回的任何内容“覆盖”。在这种情况下,一个新的功能对象。 foo
的所有元数据都被覆盖,尤其是文档字符串和函数名称。>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps为我们提供了一种方便的方法,“将文档字符串和名称提升到返回的函数。
from functools import partial, wraps
def _pseudo_decor(fun, argument):
# magic sauce to lift the name and doc of the function
@wraps(fun)
def ret_fun(*args, **kwargs):
#do stuff here, for eg.
print ("decorator arg is %s" % str(argument))
return fun(*args, **kwargs)
return ret_fun
real_decorator = partial(_pseudo_decor, argument=arg)
@real_decorator
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
评论
您的回答完美地说明了装饰器的固有正交性,谢谢
–zsf222
17 Dec 9'在15:58
@Mr_and_Mrs_D,我用functool.wraps的示例更新了帖子。在示例中添加它可能会使读者进一步困惑。
– srj
18年8月28日在2:31
这里的arg是什么!
– Stefan Falk
18-09-25在10:22
您如何将传递给bar的参数传递给real_decorator的参数?
–常昭
18年1月1日在2:56
我见过的最好的解释!
–选择
19年7月25日在6:41
#3 楼
我想展示一个想法,恕我直言非常优雅。 t.dubrownik提出的解决方案显示了一种始终相同的模式:无论装饰器做什么,都需要三层包装器。所以我认为这是元装饰器的工作,即用于装饰器的装饰器。装饰器是一个函数,它实际上可以用作带参数的常规装饰器:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
可以将其应用于常规装饰器以添加参数。例如,假设我们有一个将函数结果加倍的修饰器:
def double(f):
def aux(*xs, **kws):
return 2 * f(*xs, **kws)
return aux
@double
def function(a):
return 10 + a
print function(3) # Prints 26, namely 2 * (10 + 3)
通过
@parametrized
,我们可以构建具有参数的通用
@multiply
装饰器@parametrized
def multiply(f, n):
def aux(*xs, **kws):
return n * f(*xs, **kws)
return aux
@multiply(2)
def function(a):
return 10 + a
print function(3) # Prints 26
@multiply(3)
def function_again(a):
return 10 + a
print function(3) # Keeps printing 26
print function_again(3) # Prints 39, namely 3 * (10 + 3)
通常,参数化装饰器的第一个参数是函数,而其余参数将对应于参数化装饰器的参数。
一个有趣的用法示例可能是类型安全的断言修饰符:
import itertools as it
@parametrized
def types(f, *types):
def rep(*args):
for a, t, n in zip(args, types, it.count()):
if type(a) is not t:
raise TypeError('Value %d has not type %s. %s instead' %
(n, t, type(a))
)
return f(*args)
return rep
@types(str, int) # arg1 is str, arg2 is int
def string_multiply(text, times):
return text * times
print(string_multiply('hello', 3)) # Prints hellohellohello
print(string_multiply(3, 3)) # Fails miserably with TypeError
最后一点:这里我没有将
functools.wraps
用于包装函数,但是我建议全部使用时代。评论
没有完全使用它,而是帮助我理解了这个概念:)谢谢!
–mouckatron
17-10-10在22:04
我尝试了一下,遇到了一些问题。
–杰夫
17-10-14在14:53
@Jeff您可以与我们分享您遇到的问题吗?
–达卡夫
17-10-14在16:49
我把它与我的问题联系在一起,我确实知道了...我需要为我的特殊情况打电话给@wraps。
–杰夫
17-10-14在19:33
噢,天哪,我为此整整失去了一天。值得庆幸的是,我找到了这个答案(顺便说一句,这可能是整个互联网上创造的最好的答案)。他们也使用您的@parametrized技巧。我遇到的问题是我忘记了@语法等于实际调用(以某种方式我知道这一点,但同时又不知道您可以从我的问题中收集到)。因此,如果您想将@语法转换为平凡的调用以检查其工作原理,则最好先暂时将其注释掉,否则最终将其调用两次并得到mumbojumbo结果
–z33k
18 Mar 13 '14:50
#4 楼
这是t.dubrownik答案的稍作修改的版本。为什么?作为常规模板,应该从原始函数返回返回值。
这会更改函数的名称,这可能会影响其他修饰符/代码。
请使用
@functools.wraps()
:from functools import wraps
def decorator(argument):
def real_decorator(function):
@wraps(function)
def wrapper(*args, **kwargs):
funny_stuff()
something_with_argument(argument)
retval = function(*args, **kwargs)
more_funny_stuff()
return retval
return wrapper
return real_decorator
#5 楼
我想您的问题是将参数传递给装饰器。这有点棘手,而且不简单。下面是一个示例:
class MyDec(object):
def __init__(self,flag):
self.flag = flag
def __call__(self, original_func):
decorator_self = self
def wrappee( *args, **kwargs):
print 'in decorator before wrapee with flag ',decorator_self.flag
original_func(*args,**kwargs)
print 'in decorator after wrapee with flag ',decorator_self.flag
return wrappee
@MyDec('foo de fa fa')
def bar(a,b,c):
print 'in bar',a,b,c
bar('x','y','z')
打印件:
in decorator before wrapee with flag foo de fa fa
in bar x y z
in decorator after wrapee with flag foo de fa fa
有关更多详细信息,请参阅Bruce Eckel的文章。
评论
当心装饰器类。除非您手动重新创建实例方法描述符的逻辑,否则它们对方法无效。
–user395760
2011年5月8日18:01
delnan,想详细说明吗?我只需要使用一次这种模式,所以我还没有遇到任何陷阱。
–罗斯·罗杰斯(Ross Rogers)
2011年5月8日在18:04
@RossRogers我的猜测是@delnan指的是__name__之类的装饰器类实例所没有的东西?
–詹姆斯
2014年1月13日在17:18
@jamesc那也是,尽管相对容易解决。我所指的特定情况是类Foo:@MyDec(...)def method(self,...):等等,它不起作用,因为Foo()。method不会是绑定方法,也不会自动通过自我。也可以通过使MyDec成为描述符并在__get__中创建绑定方法来解决此问题,但它涉及更多且不那么明显。最后,装饰器类并不像看起来那样方便。
–user395760
2014年1月13日在21:49
@delnan我希望看到此警告更为突出。我正在寻找它,并且对看到确实可行的解决方案很感兴趣(虽然涉及的程度可能较小,但可能并不明显)。
– HaPsantran
16-3-13在6:42
#6 楼
def decorator(argument):
def real_decorator(function):
def wrapper(*args):
for arg in args:
assert type(arg)==int,f'{arg} is not an interger'
result = function(*args)
result = result*argument
return result
return wrapper
return real_decorator
装饰器的用途
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
然后
adder(2,3)
产生
10
但是
adder('hi',3)
生产
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-143-242a8feb1cc4> in <module>
----> 1 adder('hi',3)
<ipython-input-140-d3420c248ebd> in wrapper(*args)
3 def wrapper(*args):
4 for arg in args:
----> 5 assert type(arg)==int,f'{arg} is not an interger'
6 result = function(*args)
7 result = result*argument
AssertionError: hi is not an interger
评论
在这里的所有帖子中,这个答案对于我理解参数的传递和处理方式最有用。
–sc_props
11月22日21:12
#7 楼
编写一个可以使用和不使用参数的装饰器是一个挑战,因为在这两种情况下Python期望完全不同的行为!许多答案都试图解决此问题,以下是@ norok2对答案的改进。具体来说,这种变化消除了对locals()
的使用。 跟@ norok2给出的示例相同:
import functools
def multiplying(f_py=None, factor=1):
assert callable(f_py) or f_py is None
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return factor * func(*args, **kwargs)
return wrapper
return _decorator(f_py) if callable(f_py) 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(factor=10)
def summing(x): return sum(x)
print(summing(range(10)))
# 450
玩这段代码。
是用户必须提供键,值对参数而不是位置参数,并且保留第一个参数。
评论
这真是个天才。
– medley56
8月14日18:05
#8 楼
这是用于函数修饰器的模板,如果不提供任何参数,则不需要()
: 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
评论
还要注意,factor_or_func(或任何其他参数)永远不要在wrapper()中重新分配。
– norok2
19年8月28日在11:29
为什么需要签入locals()?
– Shital Shah
3月24日13:53
@ShitalShah涵盖了不使用()使用装饰器的情况。
– norok2
3月24日15:30
#9 楼
在我的实例中,我决定通过单行lambda来解决此问题,以创建一个新的装饰器函数:def finished_message(function, message="Finished!"):
def wrapper(*args, **kwargs):
output = function(*args,**kwargs)
print(message)
return output
return wrapper
@finished_message
def func():
pass
my_finished_message = lambda f: finished_message(f, "All Done!")
@my_finished_message
def my_func():
pass
if __name__ == '__main__':
func()
my_func()
执行后,此打印件:
Finished!
All Done!
也许不像其他解决方案那样可扩展,但是对我有用。
评论
这有效。虽然可以,但是很难将值设置给装饰器。
–阿林丹(Arindam Roychowdhury)
19年1月11日在11:28
#10 楼
众所周知,以下两段代码几乎相等:@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
一个常见的错误是认为
@
只是隐藏了最左边的参数。 @dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
如果以上是
@
的工作方式,则编写装饰器会容易得多。不幸的是,这不是完成事情的方式。考虑一个装饰器
Wait
,它会破坏程序执行几秒钟。如果不传递等待时间
,则默认值为1秒。
用例如下所示。
##################################################
@Wait
def print_something(something):
print(something)
##################################################
@Wait(3)
def print_something_else(something_else):
print(something_else)
##################################################
@Wait(delay=3)
def print_something_else(something_else):
print(something_else)
当
Wait
有一个参数时,例如@Wait(3)
,则调用Wait(3)
会在其他任何事情发生之前执行。
,即以下两段代码是等效的
@Wait(3)
def print_something_else(something_else):
print(something_else)
###############################################
return_value = Wait(3)
@return_value
def print_something_else(something_else):
print(something_else)
这是一个问题。
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
一个解决方案如下所示:
让我们从创建以下类
DelayedDecorator
开始:class DelayedDecorator:
def __init__(i, cls, *args, **kwargs):
print("Delayed Decorator __init__", cls, args, kwargs)
i._cls = cls
i._args = args
i._kwargs = kwargs
def __call__(i, func):
print("Delayed Decorator __call__", func)
if not (callable(func)):
import io
with io.StringIO() as ss:
print(
"If only one input, input must be callable",
"Instead, received:",
repr(func),
sep="\n",
file=ss
)
msg = ss.getvalue()
raise TypeError(msg)
return i._cls(func, *i._args, **i._kwargs)
现在我们可以编写如下内容:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
注意:
dec
不接受多个参数。dec
仅接受要包装的函数。 导入检查
类PolyArgDecoratorMeta(type):
def调用(等待,* args,** kwargs):
尝试:
arg_count = len (args)
如果(arg_count == 1):
如果可调用(args [0]):
超类= inspect.getmro(PolyArgDecoratorMeta)[1]
r =超类.call(等待,args [0])
else:
r = DelayedDecorator(等待,* args,** kwargs)
else:
r = DelayedDecorator(等待,* args,** kwargs)
最后:
通过
返回r
导入时间
类Wait(metaclass = PolyArgDecoratorMeta):
def init(i,func,delay = 2):
i._func = func
i._delay = delay
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
以下两段代码是等效的:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
我们可以非常缓慢地将
"something"
打印到控制台,如下所示:print_something("something")
#################################################
@Wait(delay=1)
def print_something_else(something_else):
print(something_else)
##################################################
def print_something_else(something_else):
print(something_else)
dd = DelayedDecorator(Wait, delay=1)
print_something_else = dd(print_something_else)
##################################################
print_something_else("something")
最后的注释
它看起来像很多代码,但是您不必每次都编写
DelayedDecorator
和PolyArgDecoratorMeta
类。您唯一需要亲自编写的代码如下所示,该代码相当短:from PolyArgDecoratorMeta import PolyArgDecoratorMeta
import time
class Wait(metaclass=PolyArgDecoratorMeta):
def __init__(i, func, delay = 2):
i._func = func
i._delay = delay
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
#11 楼
上面的好答案。这也说明了@wraps
,它从原始函数中获取doc字符串和函数名,并将其应用于新的包装版本: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
#12 楼
定义此“ decoratorize函数”以生成自定义的装饰函数:def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
以这种方式使用:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
#13 楼
如果函数和装饰器都必须带有参数,则可以按照以下方法进行操作。例如,有一个名为
decorator1
的装饰器带有一个参数@decorator1(5)
def func1(arg1, arg2):
print (arg1, arg2)
func1(1, 2)
现在,如果
decorator1
参数必须是动态的,或者在调用函数时传递的,则def func1(arg1, arg2):
print (arg1, arg2)
a = 1
b = 2
seconds = 10
decorator1(seconds)(func1)(a, b)
在上面的代码中
/>
seconds
是decorator1
的自变量a, b
是func1
的自变量
评论
您的示例在语法上无效。 execute_complete_reservation需要两个参数,但是您要传递一个。装饰器只是将功能包装在其他功能中的语法糖。有关完整的文档,请参见docs.python.org/reference/compound_stmts.html#function。