fal*_*cin 361 python decorator
我有装饰器传递变量'insurance_mode'的问题.我会通过以下装饰器声明来做到这一点:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
Run Code Online (Sandbox Code Playgroud)
但不幸的是,这种说法不起作用.也许有更好的方法可以解决这个问题.
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
Run Code Online (Sandbox Code Playgroud)
小智 624
你的意思是__CODE__,对吗?无论如何,带参数的装饰器的语法有点不同 - 带参数的装饰器应该返回一个函数,该函数将接受一个函数并返回另一个函数.所以它应该真正返回一个普通的装饰器.有点混乱,对吧?我的意思是:
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
Run Code Online (Sandbox Code Playgroud)
在这里,您可以阅读有关该主题的更多信息 - 也可以使用可调用对象实现此功能,并在此处进行了解释.
srj*_*srj 277
考虑带参数的装饰器的一种方法是
@decorator
def foo(*args, **kwargs):
pass
Run Code Online (Sandbox Code Playgroud)
翻译成
foo = decorator(foo)
Run Code Online (Sandbox Code Playgroud)
所以如果装饰者有参数,
@decorator_with_args(arg)
def foo(*args, **kwargs):
pass
Run Code Online (Sandbox Code Playgroud)
翻译成
foo = decorator_with_args(arg)(foo)
Run Code Online (Sandbox Code Playgroud)
decorator_with_args 是一个接受自定义参数并返回实际装饰器(将应用于修饰函数)的函数.
我使用一个简单的技巧与partials使我的装饰器容易
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
Run Code Online (Sandbox Code Playgroud)
更新:
上面,foo成为real_decorator(foo)
装饰函数的一个效果foo是在装饰器声明时重写名称.foo被任何返回的东西"覆盖" real_decorator.在这种情况下,一个新的功能对象.
foo覆盖了所有的元数据,特别是docstring和函数名.
>>> print(foo)
<function _pseudo_decor.<locals>.ret_fun at 0x10666a2f0>
Run Code Online (Sandbox Code Playgroud)
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)>
Run Code Online (Sandbox Code Playgroud)
Dac*_*cav 79
我想展示一个恕我直言非常优雅的想法.t.dubrownik提出的解决方案显示了一个始终相同的模式:无论装饰器做什么,都需要三层包装器.
所以我认为这是一个元装饰器的工作,也就是装饰器的装饰器.由于装饰器是一个函数,它实际上作为带有参数的常规装饰器:
def parametrized(dec):
def layer(*args, **kwargs):
def repl(f):
return dec(f, *args, **kwargs)
return repl
return layer
Run Code Online (Sandbox Code Playgroud)
这可以应用于常规装饰器以添加参数.例如,假设我们有装饰器,它将函数的结果加倍:
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)
Run Code Online (Sandbox Code Playgroud)
随着@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)
Run Code Online (Sandbox Code Playgroud)
通常,参数化装饰器的第一个参数是函数,而其余参数将对应于参数化装饰器的参数.
一个有趣的用法示例可能是类型安全的断言装饰器:
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
Run Code Online (Sandbox Code Playgroud)
最后一点:这里我不是functools.wraps用于包装函数,但我建议一直使用它.
Ros*_*s R 67
这是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
Run Code Online (Sandbox Code Playgroud)
Ros*_*ers 37
我认为你的问题是将参数传递给你的装饰者.这有点棘手,并不简单.
以下是如何执行此操作的示例:
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')
Run Code Online (Sandbox Code Playgroud)
打印:
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
Run Code Online (Sandbox Code Playgroud)
Shi*_*hah 23
编写一个带参数和不带参数的装饰器是一个挑战,因为 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
Run Code Online (Sandbox Code Playgroud)
问题是用户必须提供参数的键值对而不是位置参数,并且第一个参数是保留的。
Gaj*_*mbi 11
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
Run Code Online (Sandbox Code Playgroud)
装饰器的用法
@decorator(2)
def adder(*args):
sum=0
for i in args:
sum+=i
return sum
Run Code Online (Sandbox Code Playgroud)
然后
adder(2,3)
Run Code Online (Sandbox Code Playgroud)
产生
10
Run Code Online (Sandbox Code Playgroud)
但
adder('hi',3)
Run Code Online (Sandbox Code Playgroud)
产生
---------------------------------------------------------------------------
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
Run Code Online (Sandbox Code Playgroud)
Mil*_*vić 10
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print('Executed Before', original_function.__name__)
result = original_function(*args, **kwargs)
print('Executed After', original_function.__name__, '\n')
return result
return wrapper_function
@decorator_function
def display_info(name, age):
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Mr Bean', 66)
display_info('MC Jordan', 57)
Run Code Online (Sandbox Code Playgroud)
输出:
Executed Before display_info
display_info ran with arguments (Mr Bean, 66)
Executed After display_info
Executed Before display_info
display_info ran with arguments (MC Jordan, 57)
Executed After display_info
Run Code Online (Sandbox Code Playgroud)
现在让我们继续让装饰器函数接受参数。
例如,假设我想要包装器中所有这些打印语句的可自定义前缀。
现在这将是向装饰器争论的一个很好的候选者。
我们传入的参数将是该前缀。现在为了做到这一点,我们只需向装饰器添加另一个外层,因此我将其称为前缀装饰器函数。
def prefix_decorator(prefix):
def decorator_function(original_function):
def wrapper_function(*args, **kwargs):
print(prefix, 'Executed Before', original_function.__name__)
result = original_function(*args, **kwargs)
print(prefix, 'Executed After', original_function.__name__, '\n')
return result
return wrapper_function
return decorator_function
@prefix_decorator('LOG:')
def display_info(name, age):
print('display_info ran with arguments ({}, {})'.format(name, age))
display_info('Mr Bean', 66)
display_info('MC Jordan', 57)
Run Code Online (Sandbox Code Playgroud)
输出:
LOG: Executed Before display_info
display_info ran with arguments (Mr Bean, 66)
LOG: Executed After display_info
LOG: Executed Before display_info
display_info ran with arguments (MC Jordan, 57)
LOG: Executed After display_info
Run Code Online (Sandbox Code Playgroud)
LOG:前缀,您可以随时更改它。这是用于函数装饰器的模板,该模板不需要提供()任何参数即可:
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
Run Code Online (Sandbox Code Playgroud)
下面是一个示例:
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
Run Code Online (Sandbox Code Playgroud)
上面的答案很好。这也说明了@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__)
Run Code Online (Sandbox Code Playgroud)
印刷:
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
Run Code Online (Sandbox Code Playgroud)
就这么简单
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
Run Code Online (Sandbox Code Playgroud)
现在
@real_decorator(any_number_of_arguments)
def some_function(function_arguments):
return "Any"
Run Code Online (Sandbox Code Playgroud)