Python 带参数的装饰器?
我对装饰师传递变量“保险模式”有问题。我会通过以下decorator声明来实现:Python 带参数的装饰器?,python,decorator,Python,Decorator,我对装饰师传递变量“保险模式”有问题。我会通过以下decorator声明来实现: @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
@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
带参数的decorator的语法有点不同-带参数的decorator应该返回一个函数,该函数将接受一个函数并返回另一个函数。所以它应该返回一个普通的装饰器。有点困惑,对吧?我的意思是:
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
您可以阅读更多关于此主题的内容-也可以使用可调用对象实现此功能,这一点也在此处进行了说明。我猜您的问题在于将参数传递给装饰器。这有点棘手,也不简单 下面是一个如何做到这一点的示例:
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
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
编辑:要深入了解装饰师的心理模型,请看一看精彩的Pycon对话。值得花30分钟 考虑带参数的装饰器的一种方法是
@decorator
def foo(*args, **kwargs):
pass
转化为
foo = decorator(foo)
foo = decorator_with_args(arg)(foo)
如果装饰师有争论
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
转化为
foo = decorator(foo)
foo = decorator_with_args(arg)(foo)
decorator\u with_args
是一个接受自定义参数并返回实际decorator(将应用于修饰函数)的函数
我使用了一个简单的技巧来让我的装饰师变得简单
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\u decorator(foo)
修饰函数的一个效果是在修饰器声明时重写名称foo
foo
被real\u decorator
返回的任何内容“覆盖”。在这种情况下,将创建一个新的函数对象
所有foo
的元数据都被覆盖,尤其是docstring和函数名
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
>打印(foo)
为我们提供了一种方便的方法,将docstring和name“提升”到返回的函数中
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):
# pre function execution stuff here, for eg.
print("decorator argument is %s" % str(argument))
returned_value = fun(*args, **kwargs)
# post execution stuff here, for eg.
print("returned value is %s" % returned_value)
return returned_value
return ret_fun
real_decorator1 = partial(_pseudo_decor, argument="some_arg")
real_decorator2 = partial(_pseudo_decor, argument="some_other_arg")
@real_decorator1
def bar(*args, **kwargs):
pass
>>> print(bar)
<function __main__.bar(*args, **kwargs)>
>>> bar(1,2,3, k="v", x="z")
decorator argument is some_arg
returned value is None
从functools导入部分,换行
def_pseudo_装饰(有趣,有争议):
#神奇酱汁提升函数的名称和文档
@包装(乐趣)
def ret_fun(*args,**kwargs):
#函数执行前的东西在这里,例如。
打印(“装饰参数为%s”%str(参数))
返回的值=乐趣(*args,**kwargs)
#这里的处决后的东西,例如。
打印(“返回值为%s”%returned\u value)
返回值
返回ret_fun
real\u decorator1=部分(\u pseudo\u decor,argument=“some\u arg”)
real\u decorator2=部分(\u pseudo\u decor,argument=“some\u other\u arg”)
@真正的装饰师1
定义栏(*args,**kwargs):
通过
>>>打印(条)
>>>条(1,2,3,k=“v”,x=“z”)
decorator参数是一些参数
返回值为“无”
我想展示一个非常优雅的想法。t.dubrownik提出的解决方案显示了一个始终相同的模式:不管装饰器做什么,都需要三层包装器
所以我认为这是一个元装饰师的工作,也就是说,装饰师的装饰师。由于装饰器是一个函数,它实际上是一个带有参数的常规装饰器:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
这可以应用于常规装饰器以添加参数。例如,假设我们有一个decorator,它将函数的结果加倍:
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)
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()
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
通过@参数化
我们可以构建一个具有参数的通用@乘法
装饰器
@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.wrapps
,但我建议一直使用它。这里是的一个稍加修改的版本。为什么?
在我的例子中,我决定通过一行lambda来解决这个问题,以创建一个新的decorator函数:
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)
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()
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
执行时,此命令将打印:
Finished!
All Done!
也许不像其他解决方案那样可扩展,但对我来说很有用。定义此“装饰化函数”以生成定制的装饰器函数:
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)
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()
def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo
这样使用它:
@decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
装饰师的使用
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
然后
adder(2,3)
产生
10
---------------------------------------------------------------------------
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
但是
产生
10
---------------------------------------------------------------------------
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
---------------------------------------------------------------------------
AssertionError回溯(上次最近的调用)
在里面
---->1加法器('hi',3)
包装中(*args)
3 def包装(*参数):
4对于args中的arg:
---->5断言类型(arg)=int,f'{arg}不是整数'
6结果=函数(*args)
7结果=结果*参数
断言者:hi不是一个整数
这是一个函数装饰器模板,如果不提供参数,则不需要()
:
导入工具
def decorator(x_或_func=None,*decorator_args,**decorator_kws):
def_decorator(func):
@functools.wrapps(func)
def包装(*args,**kws):
如果“x_或_func”不是局部变量()\
或可调用(x_或_func)\
或x_或_func为无:
x=…# 众所周知,以下两段代码几乎是等效的:
@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
有参数时
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
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
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
@decorator1(5)
def func1(arg1, arg2):
print (arg1, arg2)
func1(1, 2)
def func1(arg1, arg2):
print (arg1, arg2)
a = 1
b = 2
seconds = 10
decorator1(seconds)(func1)(a, b)
def real_decorator(any_number_of_arguments):
def pseudo_decorator(function_to_be_decorated):
def real_wrapper(function_arguments):
print(function_arguments)
result = function_to_be_decorated(any_number_of_arguments)
return result
return real_wrapper
return pseudo_decorator
@real_decorator(any_number_of_arguments)
def some_function(function_arguments):
return "Any"
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.