我正在学习Python装饰器和内部函数,并且对我正在通过codeacademy.com https://youtu.be/WOHsHaaJ8VQ的YouTube视频学习的课程有疑问。
使用内部函数时,有时我必须返回带括号的函数,有时不带括号。
如果在不使用装饰器的情况下调用内部函数,则在返回内部函数时必须使用括号,否则似乎将内部函数作为object(?)返回。 在来自codeacademy.com的YouTube视频以及这一https://www.youtube.com/watch?v=FsAPt_9Bf3U中,他们调用了不带括号的内部函数,并输出了预期的结果。
如果我使用装饰器调用内部函数,则在返回内部函数时不必使用括号,否则它似乎可以正常工作,但会引发错误以及其他一些奇怪的结果。
我已经编写了一些代码来测试不同的变体并输出结果。 您可以在此处查看实时代码:https://trinket.io/python/af1b47658f
# Test 1: The title function returns inner function wrapper without parentheses.
def title(print_name_function):
def wrapper():
print("Professor:")
print_name_function()
return wrapper # Without parentheses
def print_my_name():
print("John")
print('Test 1')
title(print_my_name)
# Results: Nothing is printed.
# Test 2: The title function returns inner function wrapper with parentheses.
def title(print_name_function):
def wrapper():
print("Professor:")
print_name_function()
return wrapper() # With parentheses
def print_my_name():
print("John")
print('Test 2')
title(print_my_name)
# Results: Professor John is printed.
# Test 3: Using a decorator while the title function returns inner function wrapper without parentheses
def title(print_name_function):
def wrapper():
print("Professor:")
print_name_function()
return wrapper # Without parentheses
@title
def print_my_name():
print("John")
print('Test 3')
print_my_name()
# Results: Professor John is printed.
# Test 4: Using a decorator while the title function returns inner function wrapper with parentheses
def title(print_name_function):
def wrapper():
print("Professor:")
print_name_function()
return wrapper() # With parentheses
@title
def print_my_name():
print("John")
print('Test 4')
print_my_name()
# Results: Professor John is printed and the following error is thrown:
'''
Traceback (most recent call last):
File "decorators.py", line 59, in <module>
print_my_name()
TypeError: 'NoneType' object is not callable.
'''
# Additionally, Professor John is printed before 'Test 4' is printed which seems that print_my_name() runs, then print('Test 4') runs.
在上面列出的两个我看过的视频中,我发现了内部功能/装饰器...
对于内部函数:不使用括号就返回内部函数并正确运行。经过测试,我必须使用括号使其正常运行。
对于装饰器:内部函数在不使用括号的情况下返回并正确运行。经过我的测试,不使用括号即可运行。用括号运行似乎可以正常工作,但是输出顺序混乱并且抛出错误(请参见我的代码中的测试4)。
答案 0 :(得分:2)
我们将其分为两部分。
1)现在暂时忽略装饰器。
要调用某些函数,请使用括号。
没有括号,函数就是它的名字。
例如:
这是一个函数,我们给它一个数字,然后取回该数字加5。
def add_five(x):
return x + 5
我们看到add_five
(没有括号)只是函数定义。认为它是一个食谱。它不是真正的蛋糕,只是有关如何烘烤蛋糕的说明。
>>> add_five
<function add_five at 0x10da3ce18>
现在,我们给它添加成分,它会制成蛋糕:
>>> add_five(1)
6
让我们做类似的事情,但是要用更好的名字。
>>> def make_cake(cake_type):
>>> print("Making: " + cake_type + " cake!")
>>> make_cake("carrot")
'Making: carrot cake!'
>>> make_cake
<function make_cake at 0x10da3cf28>
好吧,所以当我们在函数名称前加上任何括号时,我们实际上并没有在调用函数,只是在获取函数的声明(有点像函数的出生证书,该函数具有其内存地址,在这种情况下:0x10da3cf28
。
同样的情况适用于不需要任何参数的函数。
没有括号,您只是在问,“嘿,功能存在吗?”
带有括号(和必需的参数/变量),您的意思是,“嘿,功能!”
现在是第二部分。
2)装饰者做什么?
@SyntaxVoid对您正在做的事情有很好的解释。装饰器要复杂得多,因此我将坚持解释其在特定环境下的工作。
基本上,您的装饰器@<Some Function Name>
指定一个函数来调用经过装饰的函数。
def some_decorator(function_that_I_decorated):
print("I'm going to print this, and then call my decorated function!")
function_that_I_decorated()
@some_decorator
def my_decorated_function():
print("Did I do anything?")
然后我们看到结果:
>>> def some_decorator(function_that_I_decorated):
... print("I'm going to print this, and then call my decorated function!")
... function_that_I_decorated()
...
>>> @some_decorator
... def my_decorated_function():
... print("Did I do anything?")
...
I'm going to print this, and then call my decorated function!
Did I do anything?
现在这是重要的部分:
>>> my_decorated_function
>>> my_decorated_function()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
等等...我们不是定义my_decorated_function
吗?
是的,我们定义了函数,但是装饰器正在将该函数名称重新分配给其他名称。
即my_decorator_function = some_decorator(my_decorator_function)
现在some_decorator
恰好在调用my_decorator_function
之前做了一些事情。它打印一些东西。但是some_decorator
的返回值是多少?没有return
语句,因此some_decorator
默认返回None
。
因此,my_decorator_function已创建,运行并具有新值。
我们为什么要这种行为?
当我们想要改变输出时,当多次使用相同的输入运行相同的功能时。
例如,也许我想要一个函数,该函数每调用一次就会返回“ Go Left”,或者每调用5次就会返回“ Go Right”。
如果我要使用一个具有多个变量的函数来执行此操作,那很简单!只需将其传递并检查if num_times == whatever_int
。
但是生活并不是那么容易-有时其他人已经编写了简单得多的函数,并且只允许一个变量,因为这是更通用的函数。也许它是如此复杂,以至于我们要花很长时间才能弄清楚该函数的工作原理(而且我们通常也不想违反抽象障碍)。在这种情况下,我们需要使它们的功能适应我们的需求。
我鼓励您阅读有关Currying的更多信息,因为这也会帮助您了解其他用途。