问题:带参数的装饰器?
我在装饰器传递变量’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 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
回答 0
带参数的装饰器的语法有些不同-带参数的装饰器应返回一个函数,该函数将接受一个函数并返回另一个函数。因此,它实际上应该返回一个普通的装饰器。有点混乱吧?我的意思是:
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
在这里,您可以阅读有关该主题的更多信息-也可以使用可调用对象来实现此目的,这也在那里进行了说明。
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:
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
Here you can read more on the subject – it’s also possible to implement this using callable objects and that is also explained there.
回答 1
编辑:要深入了解装饰者的心理模型,请看一下这个很棒的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元数据都将被覆盖,特别是docstring和函数名称。
>>> 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)>
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
@decorator
def foo(*args, **kwargs):
pass
translates to
foo = decorator(foo)
So if the decorator had arguments,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
translates to
foo = decorator_with_args(arg)(foo)
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
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
Update:
Above, foo becomes real_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 by real_decorator. In this case, a new function object.
All of foo‘s metadata is overridden, notably docstring and function name.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
functools.wraps gives us a convenient method to “lift” the docstring and name to the returned function.
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)>
回答 2
我想展示一个想法,恕我直言,非常优雅。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)
通常,a的第一个参数 化装饰器参数是函数,而其余参数将对应于参数化装饰器的参数。
一个有趣的用法示例可以是类型安全的断言修饰符:
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包装函数,但是我建议您一直使用它。
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:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
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:
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)
With @parametrized we can build a generic @multiply decorator having a parameter
@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)
Conventionally 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:
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
A final note: here I’m not using functools.wraps for the wrapper functions, but I would recommend using it all the times.
回答 3
这是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
Here is a slightly modified version of t.dubrownik’s answer. Why?
- As a general template, you should return the return value from the original function.
- This changes the name of the function, which could affect other decorators / code.
So use @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
回答 4
我想您的问题是将参数传递给装饰器。这有点棘手,并不简单。
这是如何执行此操作的示例:
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的文章。
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:
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')
Prints:
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
See Bruce Eckel’s article for more details.
回答 5
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
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
Usage of the decorator
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Then the
adder(2,3)
produces
10
but
adder('hi',3)
produces
---------------------------------------------------------------------------
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
回答 6
这是用于函数装饰器的模板,该模板不需要提供()任何参数即可:
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
This is a template for a function decorator that does not require () if no parameters are to be given:
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
an example of this is given below:
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
回答 7
在我的实例中,我决定通过单行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!
也许没有其他解决方案可扩展,但是为我工作。
In my instance, I decided to solve this via a one-line lambda to create a new decorator function:
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()
When executed, this prints:
Finished!
All Done!
Perhaps not as extensible as other solutions, but worked for me.
回答 8
编写一个可以使用和不使用参数的装饰器是一个挑战,因为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
玩这个代码。
要注意的是,用户必须提供键,值对参数而不是位置参数,并且保留第一个参数。
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:
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
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.
回答 9
众所周知,以下两段代码几乎等效:
@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)if(arg_count == 1):如果callable(args [0]):SuperClass = inspect。 getmro(PolyArgDecoratorMeta)[1] r =超类。呼叫(Wait,args [0])否则:r = DelayedDecorator(等待,* args,** kwargs)否则:r = DelayedDecorator(等待,* args,** kwargs)最后:通过return 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
It is well known that the following two pieces of code are nearly equivalent:
@dec
def foo():
pass foo = dec(foo)
############################################
foo = dec(foo)
A common mistake is to think that @ simply hides the leftmost argument.
@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)
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 Waitwhich haults
program 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.
##################################################
@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)
When Wait has an argument, such as @Wait(3), then the call Wait(3)
is executed before anything else happens.
That is, the following two pieces of code are equivalent
@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)
This is a problem.
if `Wait` has no arguments:
`Wait` is the decorator.
else: # `Wait` receives arguments
`Wait` is not the decorator itself.
Instead, `Wait` ***returns*** the decorator
One solution is shown below:
Let us begin by creating the following class, 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)
Now we can write things like:
dec = DelayedDecorator(Wait, delay=4)
@dec
def delayed_print(something):
print(something)
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
def __call__(i, *args, **kwargs):
time.sleep(i._delay)
r = i._func(*args, **kwargs)
return r
The following two pieces of code are equivalent:
@Wait
def print_something(something):
print (something)
##################################################
def print_something(something):
print(something)
print_something = Wait(print_something)
We can print "something" to the console very slowly, as follows:
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")
Final Notes
It may look like a lot of code, but you don’t have to write the classes DelayedDecorator and PolyArgDecoratorMeta every-time. The only code you have to personally write something like as follows, which is fairly short:
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
回答 10
定义此“ decoratorize函数”以生成定制的装饰器函数:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
使用这种方式:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
define this “decoratorize function” to generate customized decorator function:
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
use it this way:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
回答 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
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:
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__)
Prints:
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
如果函数和装饰器都必须接受参数,则可以采用以下方法。
例如,有一个名为的装饰器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
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 argument
@decorator1(5)
def func1(arg1, arg2):
print (arg1, arg2)
func1(1, 2)
Now if the decorator1 argument has to be dynamic, or passed while calling the function,
def func1(arg1, arg2):
print (arg1, arg2)
a = 1
b = 2
seconds = 10
decorator1(seconds)(func1)(a, b)
In the above code
seconds is the argument for decorator1
a, b are the arguments of func1