【问题标题】:Why does a decorator function have a return value?为什么装饰器函数有返回值?
【发布时间】:2020-06-22 17:51:47
【问题描述】:

考虑以下示例。

def decorator(function_to_decorate):
    def wrapper():
        print('Entering', function_to_decorate.__name__)
        function_to_decorate()
        print('Exiting', function_to_decorate.__name__)
    return wrapper

@decorator
def func():
    print("Original function.")

func()

由于@decorator 语法只是func = my_decorator(func) 的简写,所以my_decorator 必须返回一些东西是合乎逻辑的。我的问题是:为什么以这种方式定义装饰器而不是没有返回值:my_decorator(func)?返回包装函数wrapper的目的是什么?


编辑

除了简单的包装器之外,装饰器还能做什么?

def wrapper(function_to_decorate):
    print('Entering', function_to_decorate.__name__)
    function_to_decorate()
    print('Exiting', function_to_decorate.__name__)
def func():
    print("Original function.")
wrapper(func)

【问题讨论】:

  • 如果他们没有,那么func 仍然会引用您定义的函数。
  • @kaya3 但是为什么``` def wrapper(function_to_decorate): print('Entering', function_to_decorate.__name__) function_to_decorate() print('Exiting', function_to_decorate.__name__) wrapper(func) ```还不够?
  • 代码块在 cmets 中有效吗?
  • cmets 中的代码不能正常工作,您可以使用单反引号缩写 pieces of code。通常最好edit 你的问题。

标签: python decorator python-decorators


【解决方案1】:

想象一下,如果您可以将装饰器应用于常规变量赋值,如下所示:

def add1(x):
    return x + 1

@add1
number = 5

函数装饰器的类似行为是这样的:

number = 5
number = add1(number)

这将导致将值6 分配给变量number。现在想象一下,装饰器只是被调用而没有返回任何东西:

number = 5
add1(number)

这段代码不可能将6分配给变量number,因为numberpassed by value, not by reference;在 Python 中,函数不能将新值分配给它无法访问的完全不同范围内的变量。


def 语句实际上是一种赋值;它将函数分配给您定义它的名称。例如,函数定义def func(): pass 编译为执行STORE_NAME 的字节码,即赋值:

  1     0 LOAD_CONST         0 (<code object func at ...>)
        3 LOAD_CONST         1 ('func')
        6 MAKE_FUNCTION      0
        9 STORE_NAME         0 (func)

因此,函数装饰器的行为与上述相同,原因相同;装饰器函数无法在完全不同的范围内将新函数重新分配给变量func,因为func 是通过值传递给装饰器的,而不是通过引用传递。

func = decorator(func) 等价实际上有点误导。完全正确的是,当您使用装饰器时,您在def 语句中定义的函数将直接 传递给装饰器,而不是在传递之前分配给本地名称func。这是字节码:

  1     0 LOAD_NAME          0 (decorate)
        3 LOAD_CONST         0 (<code object func at ...>)
        6 LOAD_CONST         1 ('func')
        9 MAKE_FUNCTION      0
       12 CALL_FUNCTION      1 (1 positional, 0 keyword pair)
       15 STORE_NAME         1 (func)

一步一步:

  • decorate 函数被加载到堆栈中,
  • func 的代码对象被加载到堆栈中,然后是字符串 'func',然后 MAKE_FUNCTION 指令将这两个对象转换为一个留在堆栈中的函数。
  • CALL_FUNCTION 指令使用一个参数 func 函数调用 decorate 函数(仍在堆栈中)。
  • decorate 函数返回的任何内容都留在堆栈中,并由 STORE_NAME 指令分配给名称 func

因此,如果 decorator 函数没有返回任何内容,则将没有任何内容可分配给名称 func - 甚至不是 def 语句中的原始函数。

【讨论】:

  • 感谢您的详细解答。有关我想到的相关问题,请参阅我编辑的帖子。
  • 不同之处在于,使用装饰器时,您更改了绑定到该名称的函数的行为,以便在调用它时获得新的行为。在第二种情况下,您只是将函数传递给另一个立即调用它的函数,但该函数的名称仍以自己的行为绑定到原始函数。
猜你喜欢
  • 2016-11-09
  • 2021-06-24
  • 2018-11-20
  • 2020-12-04
  • 2020-04-28
  • 2020-02-03
  • 1970-01-01
  • 1970-01-01
  • 2012-03-30
相关资源
最近更新 更多