Python 带参数的装饰器?

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

我对装饰师传递变量“保险模式”有问题。我会通过以下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_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.