带参数的装饰器?
我在装饰器传输变量 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
I have a problem with the transfer of the variable insurance_mode
by the decorator. I would do it by the following decorator statement:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
but unfortunately, this statement does not work. Perhaps maybe there is better way to solve this problem.
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(24)
带参数的装饰器的语法有点不同 - 带参数的装饰器应该返回一个函数,该函数将接受一个函数并返回另一个函数。所以它应该返回一个正常的装饰器。有点令人困惑,对吧?我的意思是:
这里 您可以阅读有关该主题的更多信息 - 也可以使用可调用对象来实现这一点,并且那里也对此进行了解释。
The syntax for decorators with arguments is a bit different - the decorator with arguments should return a function that will take a function and return another function. So it should really return a normal decorator. A bit confusing, right? What I mean is:
Here you can read more on the subject - it's also possible to implement this using callable objects and that is also explained there.
编辑:要深入了解装饰器的心智模型,请查看此 很棒的 Pycon Talk。非常值得花30分钟。
考虑带有参数的装饰器的一种方式是
转换为
因此,如果装饰器有参数,
则转换为
decorator_with_args 是一个接受自定义参数并返回实际装饰器的函数(该函数将应用于装饰器)功能)。
我使用了一个带有部分的简单技巧来使我的装饰器变得简单
更新:
上面,
foo
变成了real_decorator(foo)
装饰函数的一个效果是名称
foo
在装饰器声明时被覆盖。foo
被real_decorator
返回的任何内容“覆盖”。在本例中,是一个新的函数对象。所有
foo
的元数据都被覆盖,特别是文档字符串和函数名称。functools.wraps 为我们提供了一种便捷的方法来“提升”返回函数的文档字符串和名称。
Edit : for an in-depth understanding of the mental model of decorators, take a look at this awesome Pycon Talk. well worth the 30 minutes.
One way of thinking about decorators with arguments is
translates to
So if the decorator had arguments,
translates to
decorator_with_args
is a function which accepts a custom argument and which returns the actual decorator (that will be applied to the decorated function).I use a simple trick with partials to make my decorators easy
Update:
Above,
foo
becomesreal_decorator(foo)
One effect of decorating a function is that the name
foo
is overridden upon decorator declaration.foo
is "overridden" by whatever is returned byreal_decorator
. In this case, a new function object.All of
foo
's metadata is overridden, notably docstring and function name.functools.wraps gives us a convenient method to "lift" the docstring and name to the returned function.
这是 t.dubrownik 的答案的稍微修改版本。为什么?
因此使用
@functools.wraps()
:Here is a slightly modified version of t.dubrownik's answer. Why?
So use
@functools.wraps()
:我想展示一个非常优雅的想法。 t.dubrownik 提出的解决方案显示了一种始终相同的模式:无论装饰器做什么,您都需要三层包装器。
所以我认为这是元装饰器的工作,即装饰器的装饰器。由于装饰器是一个函数,因此它实际上充当带参数的常规装饰器:
这可以应用于常规装饰器以添加参数。举例来说,假设我们有一个将函数结果加倍的装饰器:
使用
@parametrized
我们可以构建一个具有参数的通用@multiply
装饰器,通常第一个参数是参数化装饰器是函数,而其余参数将对应于参数化装饰器的参数。
一个有趣的使用示例可能是类型安全的断言装饰器:
最后一点:这里我没有使用 functools.wraps 来作为包装器函数,但我建议始终使用它。
I'd like to show an idea which is IMHO quite elegant. The solution proposed by t.dubrownik shows a pattern which is always the same: you need the three-layered wrapper regardless of what the decorator does.
So I thought this is a job for a meta-decorator, that is, a decorator for decorators. As a decorator is a function, it actually works as a regular decorator with arguments:
This can be applied to a regular decorator in order to add parameters. So for instance, say we have the decorator which doubles the result of a function:
With
@parametrized
we can build a generic@multiply
decorator having a parameterConventionally the first parameter of a parametrized decorator is the function, while the remaining arguments will correspond to the parameter of the parametrized decorator.
An interesting usage example could be a type-safe assertive decorator:
A final note: here I'm not using
functools.wraps
for the wrapper functions, but I would recommend using it all the times.编写一个可以使用参数和不使用参数的装饰器是一个挑战,因为 Python 在这两种情况下期望完全不同的行为!许多答案都试图解决这个问题,下面是对@norok2的答案的改进。具体来说,这种变体消除了 locals() 的使用。
遵循 @norok2 给出的相同示例:
使用此代码。
问题是用户必须提供参数的键、值对而不是位置参数,并且第一个参数被保留。
Writing a decorator that works with and without parameter is a challenge because Python expects completely different behavior in these two cases! Many answers have tried to work around this and below is an improvement of answer by @norok2. Specifically, this variation eliminates the use of
locals()
.Following the same example as given by @norok2:
Play with this code.
The catch is that the user must supply key,value pairs of parameters instead of positional parameters and the first parameter is reserved.
我认为你的问题是将参数传递给你的装饰器。这有点棘手而且不简单。
以下是如何执行此操作的示例:
打印:
请参阅 Bruce Eckel 的文章了解更多详情。
I presume your problem is passing arguments to your decorator. This is a little tricky and not straightforward.
Here's an example of how to do this:
Prints:
See Bruce Eckel's article for more details.
装饰器的使用
然后是
Produces
但是
Produces
Usage of the decorator
Then the
produces
but
produces
这是一个函数装饰器的模板,如果没有给出参数,则不需要
()
并且支持位置参数和关键字参数(但需要检查locals()
查明第一个参数是否是要修饰的函数):下面给出了一个示例:
或者,如果不需要位置参数,则可以放宽检查
wrapper()
(从而删除需要使用locals()
):下面给出了一个示例:(
部分修改自 @ShitalShah 的回答)
This is a template for a function decorator that does not require
()
if no parameters are to be given and supports both positional and keyword parameters (but requires cheching onlocals()
to find out if the first parameter is the function to be decorated or not):an example of this is given below:
Alternatively, if one does not need positional arguments, one can relax the need for checking on the first parameter within the
wrapper()
(thus removing the need to uselocals()
):an example of this is given below:
(partially reworked from @ShitalShah's answer)
就这么简单
现在
Simple as this
Now
输出:
现在让我们继续让我们的装饰器函数接受参数。
例如,假设我想要包装器中所有这些打印语句的可自定义前缀。
现在这将是装饰器参数的一个很好的候选者。
我们传入的参数将是该前缀。现在为了做到这一点,我们只需向装饰器添加另一个外层,因此我将其称为前缀装饰器函数。
输出:
LOG:
前缀,您可以随时更改它。output:
So now let's go ahead and get our decorator function to accept arguments.
For example let's say that I wanted a customizable prefix to all of these print statements within the wrapper.
Now this would be a good candidate for an argument to the decorator.
The argument that we pass in will be that prefix. Now in order to do, this we're just going to add another outer layer to our decorator, so I'm going to call this a function a prefix decorator.
output:
LOG:
prefix before our print statements in our wrapper function and you can change this any time that you want.上面的答案很好。这还说明了
@wraps
,它从原始函数中获取文档字符串和函数名称,并将其应用于新的包装版本:
Great answers above. This one also illustrates
@wraps
, which takes the doc string and function name from the original function and applies it to the new wrapped version:Prints:
例如,我在下面创建了
multiply()
,它可以接受来自装饰器的一个或没有参数或没有括号,并且我在下面创建了sum()
:现在,我将
@multiply(5)
放在sum()
上,然后调用sum(4, 6)
,如下所示:然后,我可以得到下面的结果:
接下来,我把
@multiply()
onsum()
,然后调用sum(4, 6)
如下所示:或者,我把
@在
,然后调用sum()
上相乘sum(4, 6)
如下所示:然后,我可以得到以下结果:
For example, I created
multiply()
below which can accept one or no argument or no parentheses from the decorator and I createdsum()
below:Now, I put
@multiply(5)
onsum()
, then calledsum(4, 6)
as shown below:Then, I could get the result below:
Next, I put
@multiply()
onsum()
, then calledsum(4, 6)
as shown below:Or, I put
@multiply
onsum()
, then calledsum(4, 6)
as shown below:Then, I could get the result below:
在我的例子中,我决定通过单行 lambda 来解决这个问题,以创建一个新的装饰器函数:
执行时,会打印:
也许不像其他解决方案那样可扩展,但对我有用。
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
When executed, this prints:
Perhaps not as extensible as other solutions, but worked for me.
众所周知,以下两段代码几乎是等效的:
一个常见的错误是认为
@
只是隐藏了最左边的参数。如果上述是
@
的工作方式,那么编写装饰器会容易得多。不幸的是,事情并非如此。考虑一个装饰器
Wait
,它拖拽程序执行几秒钟。
如果您没有在等待时间内通过
那么默认值为1秒。
用例如下所示。
当
Wait
有参数时,例如@Wait(3)
,则调用Wait(3)
在任何其他事情发生之前执行。
即下面两段代码是等价的,
这是一个问题。
一种解决方案如下所示:
让我们首先创建以下类
DelayedDecorator
:现在我们可以编写如下内容:
请注意:
dec
不接受多个参数。dec
仅接受要包装的函数。导入检查
类 PolyArgDecoratorMeta(类型):
def call(等等,*args,**kwargs):
尝试:
arg_count = len(参数)
如果(arg_count == 1):
如果可调用(参数[0]):
SuperClass =spect.getmro(PolyArgDecoratorMeta)[1]
r = SuperClass.call(等等,args[0])
别的:
r = DelayedDecorator(等待,*args,**kwargs)
别的:
r = DelayedDecorator(等待,*args,**kwargs)
最后:
经过
返回r
导入时间
等待类(元类=PolyArgDecoratorMeta):
def init(i, func, 延迟 = 2):
i._func = 函数
i._delay = 延迟
下面两段代码是等价的:
我们可以非常慢地打印
"something"
到控制台,如下所示:最后的注释
它可能看起来有很多代码,但你不需要不必每次都编写类
DelayedDecorator
和PolyArgDecoratorMeta
。您必须亲自编写的唯一代码如下,相当短:It is well known that the following two pieces of code are nearly equivalent:
A common mistake is to think that
@
simply hides the leftmost argument.It would be much easier to write decorators if the above is how
@
worked. Unfortunately, that’s not the way things are done.Consider a decorator
Wait
which haultsprogram execution for a few seconds.
If you don't pass in a Wait-time
then the default value is 1 seconds.
Use-cases are shown below.
When
Wait
has an argument, such as@Wait(3)
, then the callWait(3)
is executed before anything else happens.
That is, the following two pieces of code are equivalent
This is a problem.
One solution is shown below:
Let us begin by creating the following class,
DelayedDecorator
:Now we can write things like:
Note that:
dec
does not not accept multiple arguments.dec
only accepts the function to be wrapped.import inspect
class PolyArgDecoratorMeta(type):
def call(Wait, *args, **kwargs):
try:
arg_count = len(args)
if (arg_count == 1):
if callable(args[0]):
SuperClass = inspect.getmro(PolyArgDecoratorMeta)[1]
r = SuperClass.call(Wait, args[0])
else:
r = DelayedDecorator(Wait, *args, **kwargs)
else:
r = DelayedDecorator(Wait, *args, **kwargs)
finally:
pass
return r
import time
class Wait(metaclass=PolyArgDecoratorMeta):
def init(i, func, delay = 2):
i._func = func
i._delay = delay
The following two pieces of code are equivalent:
We can print
"something"
to the console very slowly, as follows:Final Notes
It may look like a lot of code, but you don't have to write the classes
DelayedDecorator
andPolyArgDecoratorMeta
every-time. The only code you have to personally write something like as follows, which is fairly short:带参数的装饰器应该返回一个函数,该函数将接受一个函数并返回另一个函数,您可以这样做,
或者您可以在第二个选项中使用 functools 模块中的部分,
只需确保传递如下参数:
the decorator with arguments should return a function that will take a function and return another function you can do that
or you can use partial from functools module
in the second option just make sure you pass the arguments like this :
它是一个可以通过多种方式调用的装饰器(在python3.7中测试):
PS感谢用户@norok2 - https://stackoverflow.com/a/57268935/5353484
UPD 用于验证参数和/或结果的装饰器类的函数和方法与注释的比较。可用于同步或异步版本:https://github.com/EvgeniyBurdin/valdec
It is a decorator that can be called in a variety of ways (tested in python3.7):
PS thanks to user @norok2 - https://stackoverflow.com/a/57268935/5353484
UPD Decorator for validating arguments and/or result of functions and methods of a class against annotations. Can be used in synchronous or asynchronous version: https://github.com/EvgeniyBurdin/valdec
假设你有一个函数
并且你想添加一个接受参数的装饰器,如下所示:
这意味着 Python 将修改
f
如下:因此,返回
decorator(msg= 'hello')
应该是一个包装函数,它接受函数 f 并返回修改后的函数。然后就可以执行修改后的函数了。所以,当你调用
f
时,就像你在做的那样:装饰器(msg='hello')(f)(args)
===
wrap(f)(args)
===modified_f(args)
但是
modified_f
可以访问传递给装饰器的kwargs
的输出
将是:
Suppose you have a function
And you want to add a decorator that accepts arguments to it like this:
This means Python will modify
f
as follows:So, the return of the part
decorator(msg='hello')
should be a wrapper function that accepts the function f and returns the modified function. then you can execute the modified function.So, when you call
f
, it is like you are doing:decorator(msg='hello')(f)(args)
===
wrap(f)(args)
===modified_f(args)
but
modified_f
has access tokwargs
passed to the decoratorThe output of
will be:
定义这个“decoratorize函数”来生成自定义的装饰器函数:
这样使用它:
define this "decoratorize function" to generate customized decorator function:
use it this way:
这是一个使用带有参数的装饰器的 Flask 示例。假设我们有一条路由'/user/name'并且我们想映射到他的主页。
输出:
Here is a Flask example using decorators with parameters. Suppose we have a route '/user/name' and we want to map to his home page.
Output:
这是柯里化函数的一个很好的用例。
柯里化函数本质上会延迟函数的调用,直到提供所有输入为止。
这可用于多种用途,例如包装器或函数式编程。在本例中,让我们创建一个接受输入的包装器。
我将使用一个简单的包 pamda ,其中包含 python 的 curry 函数。这可以用作其他函数的包装器。
安装 Pamda:
创建一个带有两个输入的简单柯里化装饰器函数:
将装饰器应用到提供给目标函数的第一个输入:
执行您的包装函数:
将所有内容放在一起:
将给出:
This is a great use case for a curried function.
Curried functions essentially delay a function from being called until all inputs have been supplied.
This can be used for a variety of things like wrappers or functional programming. In this case lets create a wrapper that takes in inputs.
I will use a simple package pamda that includes a curry function for python. This can be used as a wrapper for other functions.
Install Pamda:
Create a simple curried decorator function with two inputs:
Apply your decorator with the first input supplied to your target function:
Execute your wrapped function:
Putting everything together:
Would give:
我认为一个有效的现实世界示例以及最通用用例的使用示例在这里可能很有价值。
以下是函数的装饰器,它在进入和退出函数时打印日志。
参数控制是否打印输入输出值、日志级别等。
用法:
foo(2)
-->印刷a = A()
a.bar(1, 2, y=3)
-->印刷I think a working, real-world example, with usage examples of the most generic use-case can be valuable here.
The following is a decorator for functions, which prints to log upon entering and exiting the function.
Parameters control weather or not to print input and output values, log level and so on.
usage:
foo(2)
--> printsa = A()
a.bar(1, 2, y=3)
--> prints值得检查 使用可选参数制作装饰器 及其副本以获得更全面的答案。
带有注释和测试的通用装饰器骨架代码,试图总结所有可能的用例:
It worth checking Making decorators with optional arguments and its duplicates for more comprehensive answers.
A generic decorator skeleton code with comments and tests which tries to summarize all possible use cases:
如果函数和装饰器都必须接受参数,您可以遵循以下方法。
例如,有一个名为
decorator1
的装饰器,它接受一个参数现在如果
decorator1
参数必须是动态的,或者在调用函数时传递,在上面的代码中
times
是decorator1
的参数a, b
是func1
的参数In case both the function and the decorator have to take arguments you can follow the below approach.
For example there is a decorator named
decorator1
which takes an argumentNow if the
decorator1
argument has to be dynamic, or passed while calling the function,In the above code
seconds
is the argument fordecorator1
a, b
are the arguments offunc1
在匿名设置中使用参数进行装饰。
在多种可能性中,提出了“嵌套”语法糖修饰的两种变体。它们在目标函数的执行顺序上彼此不同,并且它们的效果通常是独立的(非交互)。
装饰器允许在目标函数执行之前或之后“注入”自定义函数。
这两个函数的调用都在元组中进行。默认情况下,返回值是目标函数的结果。
语法糖修饰
@first_internal(send_msg)('...end')
所需版本 >= 3.9,参见 PEP 614 放宽装饰器的语法限制。使用 functools.wraps 来保留目标函数的文档字符串。
输出
备注
组合装饰器,例如回拉和前推(也许用更计算机科学的术语来说:同变和反变装饰器) ,可能更有用,但需要特别注意,例如组合规则,检查哪些参数在哪里,等等
语法糖充当目标函数的一种
部分
:一旦装饰就没有回头路(没有额外的导入),但这不是强制性的,装饰器也可以以其扩展形式s使用,即first_external(send_msg)("start..." )(测试函数)(2)
使用
timeit.repeat(..., Repeat=5, number=10000)
的工作台结果,比较了经典的def
和lambda
装饰显示几乎相同:for
lambda
:[6.200810984999862, 6.035239247000391, 5.346362481000142, 5.987880147000396, 5.5331550319997405]
- mean ->5.8206
对于
def
:[6.165001932999985, 5.554595884999799, 5.798066574999666, 5.678178028000275, 5.446507932999793]
- 平均值 ->5.7284
自然地,非匿名对应物是可能的,并且提供了更大的灵活性
Decoration with parameters in an anonymous setting.
Among of the many possibilities two variations of a "nested" syntactic sugar decoration are presented. They differ from each other by the order of execution wrt to the target function and their effects are, in general, independent (non interacting).
The decorators allow an "injection" a of custom function either before or after the execution of the target function.
The calls of both functions take place in a
tuple
. As default, the return value is the result of the target function.The syntactic sugar decoration
@first_internal(send_msg)('...end')
required version >= 3.9, see PEP 614 Relaxing Grammar Restrictions On Decorators.Used
functools.wraps
to keep the doc-string of the target function.Output
Remarks
composition decorators, such as pull-back and push-forward (maybe in a more Computer Science terminology: co- and resp. contra-variant decorator), could more useful but need ad-hoc care, for example composition rules, check which parameters go where, etc
syntactic sugar acts as a kind of
partial
of the target function: once decorated there is no way back (without extra imports) but it is not mandatory, a decorator can be used also in its extended forms, i.e.first_external(send_msg)("start...")(test_function)(2)
the results of a workbench with
timeit.repeat(..., repeat=5, number=10000)
which compare the classicaldef
andlambda
decoration shows that are almost equivalent:for
lambda
:[6.200810984999862, 6.035239247000391, 5.346362481000142, 5.987880147000396, 5.5331550319997405]
- mean ->5.8206
for
def
:[6.165001932999985, 5.554595884999799, 5.798066574999666, 5.678178028000275, 5.446507932999793]
- mean ->5.7284
naturally an non-anonymous counterpart is possible and provides more flexibility