我有一个由装饰器转移变量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

当前回答

下面是一个使用带有参数的装饰器的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.

其他回答

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

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

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

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

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

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


考虑decorator Waitwhich haults 程序执行几秒钟。 如果你没有通过等待时间 缺省值为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 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

下面两段代码是等价的:

@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

匿名设置中的参数装饰。

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

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

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

语法糖装饰@first_internal(send_msg)('…end')要求版本>= 3.9,请参阅PEP 614放松对装饰器的语法限制。

functools使用。以保留目标函数的文档字符串。

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

讲话

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 classical def and lambda 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

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

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

可以这样用:

    @decoratorize(FUN, arg1 = , arg2 = , ...)
    def bar(...):
        ...

它是一个可以以多种方式调用的装饰器(在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

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

我认为这里有一个工作的、现实世界的示例,其中包含最通用的用例的使用示例。


下面是函数的装饰器,它在进入和退出函数时输出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

Foo(2)—>打印

输入foo 输出结果为(3,2)的foo

    class A():
        @log_in_out(is_print_output=False)
        def bar(self, c, m, y):
            return c, 6

a = () A.bar (1,2, y=3)—>打印

输入bar with args=(1, 2), kwargs={y:3} 离开酒吧