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

当前回答

例如,我在下面创建了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

其他回答

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

but

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

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

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

在这里你可以读到更多关于这个主题的内容——也可以使用可调用对象来实现这个功能,这里也有解释。

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

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

玩一下这段代码。

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

例如,我在下面创建了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