带有参数的装饰器?

2024-11-27 10:42:00
admin
原创
103
摘要:问题描述:我对装饰器传递变量有疑问insurance_mode。我会用以下装饰器语句来做:@execute_complete_reservation(True) def test_booking_gta_object(self): self.test_select_gta_object() 但不幸的是...

问题描述:

我对装饰器传递变量有疑问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

解决方案 1:

带参数的装饰器的语法有点不同 - 带参数的装饰器应该返回一个函数,该函数将接受一个函数并返回另一个函数。所以它实际上应该返回一个普通的装饰器。有点令人困惑,对吧?我的意思是:

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

在这里您可以阅读有关该主题的更多信息 - 也可以使用可调用对象来实现这一点,并且也在那里进行了解释。

解决方案 2:

编辑:为了深入了解装饰器的心理模型,请看一下这个精彩的 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元数据都被覆盖,特别是文档字符串和函数名称。

>>> 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

解决方案 3:

这是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

解决方案 4:

我想展示一个我认为相当优雅的想法。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)

惯例,参数化装饰器的第一个参数是函数,而其余参数将对应于参数化装饰器的参数。

一个有趣的使用示例可能是类型安全的断言装饰器:

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包装函数,但我建议始终使用它。

解决方案 5:

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

玩一下这个代码。

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

解决方案 6:

我猜你的问题是向装饰器传递参数。这有点棘手,而且不太简单。

以下是如何执行此操作的示例:

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 的文章。

解决方案 7:

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

解决方案 8:

这是一个函数装饰器的模板,它不需要()给出任何参数,并且支持位置参数和关键字参数(但需要检查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() \n                    or callable(x_or_func) \n                    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() \n                    or callable(factor_or_func) \n                    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 的答案重新编写)

解决方案 9:

就这么简单

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"

解决方案 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__, '
')
        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 
  • 现在让我们继续让装饰函数接受参数。

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

  • 现在这将成为装饰器参数的一个很好的候选。

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

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

解决方案 11:

上面的答案很棒。这个还说明了@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

解决方案 12:

例如,我创建了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)

或者,我放入@multiplysum()然后调用sum(4, 6),如下所示:

# 4 + 6 = 10

@multiply # Here
def sum(num1, num2):
    return num1 + num2
    
result = sum(4, 6)
print(result)

然后,我可以得到以下结果:

10

解决方案 13:

在我的例子中,我决定通过一行 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!

也许不像其他解决方案那样具有扩展性,但对我来说还是有用的。

解决方案 14:

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

@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="
",
                    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 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 返回 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")

最后说明

代码可能看起来很多,但你不必每次都编写类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

解决方案 15:

带有参数的装饰器应该返回一个函数,该函数将接受一个函数并返回另一个函数,你可以这样做

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

或者你可以使用 functools 模块中的 partial

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

解决方案 16:

它是一个装饰器,有多种调用方式(在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]) \n        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

解决方案 17:

假设你有一个函数

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,就像您正在做的一样:
decorator(msg='hello')(f)(args)
=== wrap(f)(args)===modified_f(args)
modified_f可以访问kwargs传递给装饰器

输出

f(1,2,3)

将:

hello
(1, 2, 3)

解决方案 18:

定义这个“装饰函数”来生成定制的装饰函数:

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

请按如下方式使用:

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

解决方案 19:

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

解决方案 20:

这是柯里化函数的一个很好的用例。

柯里化函数本质上是延迟调用一个函数,直到所有输入都已提供为止。

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

我将使用一个简单的包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!

解决方案 21:

我认为一个可行的、真实的例子,包含最通用用例的使用示例在这里是有价值的。


以下是函数的装饰器,它在进入和退出函数时打印到日志。

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

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,

退出 foo,结果为 (3,2)

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

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

使用 args=(1, 2),kwargs={y:3} 进入 bar,

退出 bar

解决方案 22:

值得检查使用可选参数制作装饰器及其重复项以获得更全面的答案。

带有注释和测试的通用装饰器骨架代码,试​​图总结所有可能的用例:

from functools import wraps


def my_decorator(*args_or_func, **decorator_kwargs):
    """Originally from:
     https://stackoverflow.com/questions/5929107/decorators-with-parameters/65512042#65512042
    with hints from:
     https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments/24617244#24617244"""
    def _decorator(func):
        @wraps(func)  # See: https://docs.python.org/3/library/functools.html#functools.wraps
        def wrapper(*args, **kwargs):
            # Do things before executing the function
            print("Available inside the wrapper:", decorator_args, decorator_kwargs)

            # Execute the original function with its args
            result = func(*args, **kwargs)

            # Do more things after executing the function

            return result

        return wrapper

    # TODO If you need only @my_decorator() or @my_decorator(...) use the else branch only
    # TODO To mitigate TypeError on single callable positional argument allow only keyword arguments:
    #  def my_decorator(func=None, *, decorator_kwarg1='value1', decorator_kwarg2='value2'):  # No **kwargs possible!
    #  and below: if func is not None: _decorator(func) else: _decorator
    # To allow @my_decorator (i.e. without parentheses) WARNING: Use keyword argument for single callable parameter!
    if len(args_or_func) == 1 and len(decorator_kwargs) == 0 and callable(args_or_func[0]):
        # Here you can set default values for positional arguments
        decorator_args = ()
        return _decorator(args_or_func[0])
    else:
        decorator_args = args_or_func  # This "global" variable is used inside _decorator() which is defined above
        return _decorator              # Hint: The function's implementation is evaluated when the function executed


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


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


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


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


# Single callable positional argument. BAD INVOCATION! -> TypeError
@my_decorator(lambda x: 42)
def func_3(arg): print(arg)


try:
    func_3("Single callable positional argument. BAD INVOCATION! -> TypeError")
except TypeError as e:
    print("test3")
    print(f"Catched TypeError: TypeError: {e}")
# test3
# Catched TypeError: TypeError: 'int' object is not callable


@my_decorator(lambda x: 42, "any arg")
def func_4(arg): print(arg)


func_4("test4")
# Available inside the wrapper: (<function <lambda> at 0x7f96d94ed870>, 'any arg') {}
# test4


@my_decorator(lambda x: 42, kw="any arg")
def func_5(arg): print(arg)


func_5("test5")
# Available inside the wrapper: (<function <lambda> at 0x7f2dbae3d870>,) {'kw': 'any arg'}
# test5


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


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


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


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

解决方案 23:

如果函数和装饰器都必须接受参数,则可以遵循以下方法。

例如有一个名为的装饰器,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

解决方案 24:

在匿名设置中使用参数进行装饰。

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

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

两个函数的调用都发生在 中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:\n           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:\n           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

评论

  • 组合装饰器,例如拉回和推进(也许用更计算机科学的术语来说:同变和逆变装饰器),可能更有用,但需要特别注意,例如组合规则,检查哪些参数放到哪里,等等

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

  • timeit.repeat(..., repeat=5, number=10000)与古典def和装饰进行比较的工作台结果lambda表明几乎是相同的:

+ 对于`lambda`:`[6.200810984999862, 6.035239247000391, 5.346362481000142, 5.987880147000396, 5.5331550319997405]`- 平均值 ->`5.8206`
+ 对于`def`:`[6.165001932999985, 5.554595884999799, 5.798066574999666, 5.678178028000275, 5.446507932999793]`- 平均值 -> `5.7284`
  • 当然,非匿名的对应方也是可能的,并且提供更多的灵活性

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1043  
  IPD(Integrated Product Development,集成产品开发)是一种系统化的产品开发方法论,旨在通过跨职能团队的协作,优化产品开发的效率和质量。IPD流程强调从市场需求出发,通过并行工程、跨部门协作和阶段性评审,确保产品从概念到上市的每个环节都高效且可控。随着敏捷开发方法的普及,越来越多的企业开始...
华为IPD流程   41  
  随着企业产品开发复杂度的提升以及市场需求的快速变化,传统的产品开发模式逐渐显现出局限性。集成产品开发(IPD)流程与敏捷开发(Agile Development)作为两种主流的开发方法论,分别从系统化管理和快速响应需求的角度为企业提供了解决方案。然而,单独使用其中一种方法往往无法完全满足企业在效率、质量和创新上的多重需...
华为IPD流程   35  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司成功的关键因素之一。它不仅帮助华为在技术上实现了快速创新,还通过市场导向确保了产品的商业成功。IPD流程通过整合技术与市场双驱动,实现了从需求定义到产品交付的全生命周期管理。这种模式不仅提高了产品的开发效率,还降低了市...
IPD流程中PDCP是什么意思   32  
  在研发领域,集成产品开发(IPD)流程已经成为企业提升创新效率和市场竞争力的重要手段。然而,资源分配的不合理往往是制约IPD流程效率的关键因素之一。无论是人力资源、财务资源还是技术资源,如何高效分配直接关系到项目的成功与否。优化资源分配不仅能够缩短产品开发周期,还能降低研发成本,提升产品的市场竞争力。因此,掌握资源分配...
IPD流程中CDCP   34  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用