有参数的装饰器?

我有一个问题,通过装饰器转移变量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
443469 次浏览

带参数的装饰器的语法有点不同——带参数的装饰器应该返回一个取一个函数的函数,并返回另一个函数。它应该返回一个普通的装饰器。有点困惑,对吧?我的意思是:

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

详情见Bruce Eckel的文章

编辑:要深入了解装饰器的心理模型,请查看精彩的Pycon Talk。这30分钟很值得。

考虑带参数的装饰器的一种方式是

@decorator
def foo(*args, **kwargs):
pass

翻译为

foo = decorator(foo)

如果decorator有参数,

@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):
# 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

我想展示一个想法,在我看来很优雅。t.dubrownik提出的解决方案显示了一个始终相同的模式:无论装饰器做什么,您都需要三层包装器。

所以我认为这是一个元装饰师的工作,也就是说,装饰师的装饰师。由于decorator是一个函数,它实际上是一个带有参数的常规decorator:

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)

使用@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,但我建议始终使用它。

下面是稍微修改过的t.dubrownik的回答版本。为什么?

  1. 作为通用模板,您应该返回原始函数的返回值。
  2. 这将改变函数的名称,这可能会影响其他装饰器/代码。

所以使用@functools.wraps():

from functools import wraps


def create_decorator(argument):
def 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 decorator

在我的实例中,我决定通过一行lambda来解决这个问题,以创建一个新的decorator函数:

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!

也许不像其他解决方案那样可扩展,但对我来说是可行的。

定义这个decoratorize函数来生成定制的decorator函数:

def decoratorize(FUN, **kw):
def foo(*args, **kws):
return FUN(*args, **kws, **kw)
return foo

可以这样用:

    @decoratorize(FUN, arg1 = , arg2 = , ...)
def bar(...):
...
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

这是一个函数装饰器模板,如果没有参数,它不需要(),并且支持位置参数和关键字参数(但需要检查locals(),以确定第一个参数是否是要装饰的函数):

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

或者,如果不需要位置参数,可以放松检查wrapper()内的第一个参数的需要(因此不需要使用locals()):

import functools




def decorator(func_=None, **decorator_kws):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kws):
return func(*args, **kws)
return wrapper


if callable(func_):
return _decorator(func_)
elif func_ is None:
return _decorator
else:
raise RuntimeWarning("Positional arguments are not supported.")

下面是一个例子:

import functools




def multiplying(func_=None, factor=1):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return factor * func(*args, **kwargs)
return wrapper


if callable(func_):
return _decorator(func_)
elif func_ is None:
return _decorator
else:
raise RuntimeWarning("Positional arguments are not supported.")




@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




@multiplying(10)
def summing(x): return sum(x)
print(summing(range(10)))
# RuntimeWarning Traceback (most recent call last)
#    ....
# RuntimeWarning: Positional arguments are not supported.

(部分从@ShitalShah的回答重做)

众所周知,下面两段代码几乎是等价的:

@dec
def foo():
pass    foo = dec(foo)


############################################
foo = dec(foo)

一个常见的错误是认为@只是隐藏了最左边的参数。

@dec(1, 2, 3)
def foo():
pass
###########################################
foo = dec(foo, 1, 2, 3)

如果上面是@的工作方式,那么编写装饰器会容易得多。不幸的是,事情不是这样的。


考虑一个装饰器# eyz0 程序执行几秒钟。 如果你没有通过等待时间 缺省值为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只接受要包装的函数。

    < p >进口检查 类PolyArgDecoratorMeta(类型): def 调用(等待,*args, **kwargs): 试一试: Arg_count = len(args) If (arg_count == 1): 如果调用(args [0]): SuperClass = inspect.getmro(PolyArgDecoratorMeta)[1] r =超类。# EYZ0(等等,args [0]) 其他: r = DelayedDecorator(等待,*args, **kwargs) 其他: r = DelayedDecorator(等待,*args, **kwargs) 最后: 通过 返回r < / p > < p >导入时间 类等(元类= PolyArgDecoratorMeta): def 初始化(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)

我们可以非常缓慢地将"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")

最后指出

它可能看起来有很多代码,但您不必每次都编写DelayedDecoratorPolyArgDecoratorMeta类。你必须亲自编写的代码如下所示,这是相当短的:

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

上面的回答很棒。这个例子还演示了@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__)

打印:

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

编写一个带参数和不带参数的装饰器是一个挑战,因为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

# EYZ0。

问题是用户必须提供键、值对的参数,而不是位置参数,并且第一个参数是保留的。

如果函数和装饰器都必须接受参数,可以采用下面的方法。

例如,有一个名为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)

在上面的代码中

  • secondsdecorator1的参数
  • a, bfunc1的参数

它是一个可以以多种方式调用的装饰器(在python3.7中测试):

import functools




def my_decorator(*args_or_func, **decorator_kwargs):


def _decorator(func):


@functools.wraps(func)
def wrapper(*args, **kwargs):


if not args_or_func or callable(args_or_func[0]):
# Here you can set default values for positional arguments
decorator_args = ()
else:
decorator_args = args_or_func


print(
"Available inside the wrapper:",
decorator_args, decorator_kwargs
)


# ...
result = func(*args, **kwargs)
# ...


return result


return wrapper


return _decorator(args_or_func[0]) \
if args_or_func and callable(args_or_func[0]) else _decorator




@my_decorator
def func_1(arg): print(arg)


func_1("test")
# Available inside the wrapper: () {}
# test




@my_decorator()
def func_2(arg): print(arg)


func_2("test")
# Available inside the wrapper: () {}
# test




@my_decorator("any arg")
def func_3(arg): print(arg)


func_3("test")
# Available inside the wrapper: ('any arg',) {}
# test




@my_decorator("arg_1", 2, [3, 4, 5], kwarg_1=1, kwarg_2="2")
def func_4(arg): print(arg)


func_4("test")
# Available inside the wrapper: ('arg_1', 2, [3, 4, 5]) {'kwarg_1': 1, 'kwarg_2': '2'}
# test


PS感谢用户@norok2 - https://stackoverflow.com/a/57268935/5353484

乌利希期刊指南装饰器,用于根据注释验证类的函数和方法的参数和/或结果。可用于同步或异步版本:https://github.com/EvgeniyBurdin/valdec

enter image description here

  • 在这里,我们用两个不同的名称和两个不同的年龄运行了两次display info。
  • 现在,每当我们运行display info时,我们的装饰器还添加了打印换行函数前后一行的功能。
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)

输出:

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
  • 现在让我们继续,让decorator函数接受参数。

  • 例如,假设我想为包装器中的所有这些打印语句添加一个可定制的前缀。

  • 现在这将是decorator参数的一个很好的候选者。

  • 我们传入的参数将是该前缀。现在为了做到这一点,我们将添加另一个外层到我们的装饰器中,我将把这个函数称为前缀装饰器。

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)

输出:

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
  • 现在我们在包装器函数的print语句之前有了LOG:前缀,您可以随时更改它。

如此简单

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"

下面是一个使用带有参数的装饰器的Flask示例。假设我们有一个路由'/user/name',我们想要映射到他的主页。

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.

这是curry函数的一个很好的用例。

curry函数本质上是延迟函数的调用,直到提供了所有输入。

这可以用于各种事情,如包装器或函数式编程。在本例中,让我们创建一个接受输入的包装器。

我将使用一个简单的包pamda,其中包含python的curry函数。这可以用作其他函数的包装器。

安装Pamda:

pip install pamda

创建一个简单的带有两个输入的装饰函数:

@pamda.curry()
def my_decorator(input, func):
print ("Executing Decorator")
print(f"input:{input}")
return func

使用提供给目标函数的第一个输入应用你的装饰器:

@my_decorator('Hi!')
def foo(input):
print('Executing Foo!')
print(f"input:{input}")

执行你的包装函数:

x=foo('Bye!')

把所有东西放在一起:

from pamda import pamda


@pamda.curry()
def my_decorator(input, func):
print ("Executing Decorator")
print(f"input:{input}")
return func


@my_decorator('Hi!')
def foo(input):
print('Executing Foo!')
print(f"input:{input}")


x=foo('Bye!')

将:

Executing Decorator
input:Hi!
Executing Foo!
input:Bye!

我认为最通用的用例工作,现实世界的例子,使用示例在这里很有价值。


下面是函数的装饰器,它在进入和退出函数时输出log。

参数控制是否打印输入输出值,日志级别等。

import logging
from functools import wraps




def log_in_out(logger=logging.get_logger(), is_print_input=True, is_print_output=True, is_method=True, log_level=logging.DEBUG):
"""
@param logger-
@param is_print_input- toggle printing input arguments
@param is_print_output- toggle printing output values
@param is_method- True for methods, False for functions. Makes "self" not printed in case of is_print_input==True
@param log_level-


@returns- a decorator that logs to logger when entering or exiting the decorated function.
Don't uglify your code!
"""


def decor(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
if is_print_input:
logger.log(
msg=f"Entered {fn.__name__} with args={args[1:] if is_method else args}, kwargs={kwargs}",
level=log_level
)
else:
logger.log(
msg=f"Entered {fn.__name__}",
level=log_level
)


result = fn(*args, **kwargs)


if is_print_output and result is not None:
logger.log(
msg=f"Exited {fn.__name__} with result {result}",
level=log_level,
)
else:
logger.log(
msg=f"Exited {fn.__name__}",
level=log_level
)


return result


return wrapper


return decor


用法:

 @log_in_out(is_method=False, is_print_input=False)
def foo(a, b=5):
return 3, a

# EYZ0——比;打印

输入foo
退出foo与result (3,2)

    class A():
@log_in_out(is_print_output=False)
def bar(self, c, m, y):
return c, 6
< p > # EYZ0 # EYZ0——比;打印< / p >

输入bar with args=(1,2), kwargs={y:3}
走出酒吧< / p >

带参数的装饰器应该返回一个函数,该函数将接受一个函数,并返回另一个函数

def decorator_factory(argument):
def decorator(function):
def wrapper(*args, **kwargs):
"""
add somhting
"""
return  function(*args, **kwargs)
return wrapper
return decorator

或者你可以使用functools模块的部分

def decorator(function =None,*,argument ):
if function is None :
return partial(decorator,argument=argument)
def wrapper(*args, **kwargs):
"""
add somhting
"""
return  function(*args, **kwargs)
return wrapper

在第二个选项中,确保你像这样传递参数:

@decorator(argument = 'args')
def func():
pass

假设你有一个函数

def f(*args):
print(*args)

你想要添加一个接受参数的装饰器,就像这样:

@decorator(msg='hello')
def f(*args):
print(*args)

这意味着Python将修改f,如下所示:

f = decorator(msg='hello')(f)

因此,部分decorator(msg='hello')的返回值应该是一个包装器函数,它接受函数f并返回修改后的函数。然后可以执行修改后的函数。

def decorator(**kwargs):
def wrap(f):
def modified_f(*args):
print(kwargs['msg']) # use passed arguments to the decorator
return f(*args)
return modified_f
return wrap
所以,当你调用f时,就像你在做: # EYZ0 === # eyz0 === # eyz1 但是modified_f可以访问kwargs传递给装饰器

的输出

f(1,2,3)

将会是:

hello
(1, 2, 3)

匿名设置中的参数装饰。

在许多可能的“嵌套”的两种变体中;介绍了句法糖装饰。它们之间的区别在于执行wrt到目标函数的顺序,并且它们的效果通常是独立的(不相互作用)。

装饰师允许“注入”;在目标函数执行之前或之后执行自定义函数。

两个函数的调用都发生在tuple中。默认情况下,返回值是目标函数的结果。

语法糖装饰@first_internal(send_msg)('...end')需要的版本>= 3.9,见PEP 614放宽装饰符的语法限制。

使用functools.wraps保存目标函数的文档字符串。

from functools import wraps




def first_external(f_external):
return lambda *args_external, **kwargs_external:\
lambda f_target: wraps(f_target)(
lambda *args_target, **kwargs_target:
(f_external(*args_external, **kwargs_external),
f_target(*args_target, **kwargs_target))[1]
)




def first_internal(f_external):
return lambda *args_external, **kwargs_external:\
lambda f_target: wraps(f_target)(
lambda *args_target, **kwargs_target:
(f_target(*args_target, **kwargs_target),
f_external(*args_external, **kwargs_external))[0]
)




def send_msg(x):
print('msg>', x)




@first_internal(send_msg)('...end')    # python >= 3.9
@first_external(send_msg)("start...")  # python >= 3.9
def test_function(x):
"""Test function"""
print('from test_function')
return x




test_function(2)

输出

msg> start...
from test_function
msg> ...end

讲话

  • 作文装饰符,例如后拉和前推(可能在计算机科学术语中:co-和resp)。逆变装饰器),可能更有用,但需要特别注意,例如组合规则,检查哪些参数去哪里,等等

  • 语法糖充当目标函数的partial:一旦装饰就没有返回的方法(没有额外的导入),但这不是强制性的,装饰器也可以在它的扩展形式年代中使用,即first_external(send_msg)("start...")(test_function)(2)

  • 使用timeit.repeat(..., repeat=5, number=10000)的工作台的结果,与经典的deflambda装饰进行比较,显示几乎相同:

    • : lambda: [6.200810984999862, 6.035239247000391, 5.346362481000142, 5.987880147000396, 5.5331550319997405] - mean -># EYZ2

    • : def: [6.165001932999985, 5.554595884999799, 5.798066574999666, 5.678178028000275, 5.446507932999793] - mean -># EYZ2

  • 自然,非匿名对等体是可能的,并提供了更大的灵活性

例如,我在下面创建了multiply(),它可以接受装饰器的一个参数或没有参数或没有括号,我在下面创建了sum():

from numbers import Number


def multiply(num=1):
def _multiply(func):
def core(*args, **kwargs):
result = func(*args, **kwargs)
if isinstance(num, Number):
return result * num
else:
return result
return core
if callable(num):
return _multiply(num)
else:
return _multiply


def sum(num1, num2):
return num1 + num2

现在,我把@multiply(5)放在sum()上,然后称为sum(4, 6),如下所示:

# (4 + 6) x 5 = 50


@multiply(5) # Here
def sum(num1, num2):
return num1 + num2


result = sum(4, 6)
print(result)

那么,我可以得到如下结果:

50

接下来,我把@multiply()放在sum()上,然后称为sum(4, 6),如下所示:

# (4 + 6) x 1 = 10


@multiply() # Here
def sum(num1, num2):
return num1 + num2
    

result = sum(4, 6)
print(result)

或者,我把@multiply放在sum()上,然后称为sum(4, 6),如下所示:

# 4 + 6 = 10


@multiply # Here
def sum(num1, num2):
return num1 + num2
    

result = sum(4, 6)
print(result)

那么,我可以得到如下结果:

10