我正在教自己如何在一些在线教程的帮助下编写代码。我遇到了“装饰者”,我似乎可以理解它是如何工作的但是有些困扰我。这是给出的代码:
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'对象不可调用。
当我指定了修饰变量的值时。为什么我还必须使用“print_text”而不是“print_text()”,否则如果我这样做会引发相同的TypeError?
当我使用变量“装饰”时。为什么我必须像函数一样调用它(最后添加())。当我用“装饰”或“打印(装饰)”来称它时,它会说完全不同的东西吗?
抱歉愚蠢的问题。但我刚刚开始,所以请耐心等待。另外,请让您的回复初学者友好。谢谢
答案 0 :(得分:7)
在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()
执行完全相同的操作,在函数内运行代码。
这就是func
内部decor()
所指的名称;它是对同一个函数对象的引用。你用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
),以便将来当您调用decorated()
,在调用原始函数之前和之后调用该包装函数执行额外操作(通过引用func
的{{1}}名称。)
所以print_text()
做的是返回一个 new 函数对象,一个打印的东西,调用传入的函数对象,然后打印其他东西。然后可以使用该新函数对象替换旧的函数对象。