装饰器,调用不带括号的函数?

Posted

技术标签:

【中文标题】装饰器,调用不带括号的函数?【英文标题】:Decorators, calling functions without parenthesis? 【发布时间】:2017-10-17 02:15:09 【问题描述】:

我正在借助一些在线教程自学如何编码。我遇到过“装饰者”,我似乎可以理解它是如何工作的,但有些事情让我很困扰。这是给出的代码:

def decor(func):
    def wrap():
        print("-----------")
        func()
        print("-----------")
    return wrap

def print_text():
    print("Hello World")

decorated = decor(print_text)
decorated()

output:
-----------
Hello World
-----------

我想了解的事情是:

为什么必须调用“return wrap”而不是“return wrap()”?而如果你不这样做,你会得到一个“TypeError: 'NoneType' object is not callable.

当我分配修饰变量的值时。为什么我还必须使用“print_text”而不是“print_text()”,而如果我这样做会引发相同的 TypeError?

当我使用变量“decorated”时。为什么我必须像函数一样调用它(在末尾添加())。当我使用“decorated”或“print(decorated)”来称呼它时,它说的是完全不同的东西?

对不起,愚蠢的问题。但我才刚刚开始,所以请多多包涵。另外,请让您的回答对初学者友好。谢谢你

【问题讨论】:

【参考方案1】:

在 Python 中,几乎所有东西都是对象。函数也是对象。你可以通过他们的名字来引用他们:

>>> def print_text():
...     print("Hello World")
...
>>> print_text   # **no** call here, this is just referencing the object
<function print_text at 0x10e3f1c80>
>>> print_text() # With a call, so now we *run* the function
Hello World

在名称中添加() 告诉 Python 调用函数,这导致它实际执行函数体,没有调用,它只是向您显示名称引用的内容。

您也可以将函数对象分配给其他名称。仍然可以调用其他名称,调用函数:

>>> other_name = print_text
>>> other_name
<function print_text at 0x10e3f1c80>
>>> other_name()
Hello World

所以other_name 只是对同一对象的另一个引用,添加()(调用表达式)会导致函数对象被执行。 print_text()other_name() 做同样的事情,在函数内部运行代码。

这就是 decor() 内部的名称 func 所指的;它是对同一个函数对象的引用。你用decor(print_text) 传递了它。只是稍后,在wrapper() 内部,表达式func() 调用该函数对象。如果您改为传入print_text(),您将传入该函数返回的None 对象,并且无法调用None

>>> return_value = print_text()
Hello World
>>> return_value is None
True
>>> return_value()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable

接下来,return wrapper 将新创建的wrapper 函数对象返回给调用者。如果您执行return wrapper(),您将返回函数的结果,而不是函数对象本身。

装饰器的全部意义在于将原始函数对象替换为执行额外操作的新对象,这就是为什么装饰器返回该替换对象(在您的示例中为 wrapper),以便将来当您调用 @ 987654341@,您调用该包装函数在调用原始函数之前和之后做一些额外的事情(通过引用print_text()func 名称)。

所以decor(some_function) 所做的是返回一个new 函数对象,它会打印一些东西,调用传入的函数对象,然后打印其他东西。然后可以使用新的函数对象来替换旧的函数对象。

【讨论】:

以上是关于装饰器,调用不带括号的函数?的主要内容,如果未能解决你的问题,请参考以下文章

关于Python装饰器内层函数为什么要return目标函数的一些个人见解

使用带或不带括号的 python 装饰器

带参数的装饰器

python装饰器

python---装饰器

Python装饰器