Python 装饰器未按预期运行

Python decorator not acting as expected

只是在玩装饰器,还有我做的一个简单的例子。我期望每次调用方法时,方法名称都会被添加到列表中。

python_func_calls = []

def log_func_call(func):
    python_func_calls.append(func.__name__)
    return func

@log_func_call
def print_a():
    print('I am the a function...')
    
@log_func_call
def print_b():
    print('I am the b function...')
    
print_a()
print_b()
print_b()
print_a()

print(python_func_calls)

但这给了我 python_func_calls 的以下内容:

['print_a', 'print_b']

我原以为列表中会有 4 个条目,因为装饰函数被调用了 4 次。

您需要创建一个新函数和 return that 装饰器。在此函数内,调用传入的包装函数。例如:

from functools import wraps

python_func_calls = []

def log_func_call(func):
    @wraps(func)
    def wrapped(*args, **kwargs):
        python_func_calls.append(func.__name__)
        func(*args, **kwargs)
    return wrapped

@log_func_call
def print_a():
    print('I am the a function...')
    
@log_func_call
def print_b():
    print('I am the b function...')
    
print_a()
print_b()
print_b()
print_a()

print(python_func_calls)

这使得 python_func_calls 为:

['print_a', 'print_b', 'print_b', 'print_a']

这样做的原因是装饰器 returned 的函数替换了包装函数。所以它每次都会被调用,而不仅仅是当装饰器是 created.In 你的原始代码时,你 return 原始函数......所以当你调用函数时没有任何改变。

装饰器在函数定义处被调用所以总共调用了两次:

python_func_calls = []

def log_func_call(func):
    python_func_calls.append(func.__name__)
    return func

@log_func_call #first call
def print_a():
    print('I am the a function...')
    
@log_func_call #second call
def print_b():
    print('I am the b function...')
    
print_a() #decorator not called
print_b() #decorator not called
print_b() #decorator not called
print_a() #decorator not called

print(python_func_calls)

所以尽管有 4 个函数调用,但只有 2 个装饰器调用。

装饰器语法类似于:

def print_a():
    ...

print_a = log_func_call(print_a)

因此,装饰器仅以目标函数作为参数被调用一次。如果您希望装饰版本为每个函数调用做额外的工作,您需要在装饰器中创建一个包装函数来做两件事:

  1. 做额外的工作:python_func_calls.append(func.__name__)
  2. 调用装饰函数和 return 无论那个函数 returns: return func(*args, **kwargs).

以下装饰器实现了这些要求:

from functools import wraps

python_func_calls = []

def log_func_call(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        python_func_calls.append(func.__name__)
        return func(*args, **kwars)

    return wrapper

请注意,装饰器使用另一个装饰器 wraps,它调整 wrapper 函数的元数据以模仿装饰函数 func 的元数据(例如签名和文档字符串)。