Python inner functions/decorators: 返回内部函数时什么时候应该使用括号?

Python inner functions/decorators: When should I use parentheses when returning an inner function?

我正在学习 Python 装饰器和内部函数,并且对我通过来自 codeacademy.com https://youtu.be/WOHsHaaJ8VQ.

的 YouTube 视频学习的课程有一些疑问

使用内部函数时,有时我必须 return 带括号的函数,有时不带括号。

如果我在不使用装饰器的情况下调用内部函数,我必须在 returning 内部函数时使用括号,否则看起来内部函数是 returned 作为对象(?) . 在 codeacademy.com 和这个 https://www.youtube.com/watch?v=FsAPt_9Bf3U 的 YouTube 视频中,他们调用了不带括号的内部函数,并输出了预期的结果。

如果我使用装饰器调用内部函数,则在 return 内部函数时我必须不使用括号,否则它似乎可以正常工作,但会抛出错误以及其他一些奇怪的结果。

我编写了一些代码来测试不同的变体并输出结果。 您可以在此处查看实时代码: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.

在上面列出的关于内部的两个视频中 functions/decorators 我发现...

对于内部函数:内部函数 returned 没有使用括号并且 运行 正确。根据我的测试,我必须使用括号才能正确 运行。

对于装饰器:内部函数 returned 没有使用括号并且 运行 正确。根据我的测试, 运行ning 不使用括号是有效的。 运行 括号似乎有效,但输出顺序混淆并引发错误(请参阅我的代码中的测试 4)。

让我们把它分成两部分。

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.

同样的事情适用于不需要任何参数的函数。

  • 没有括号,你只是在问,"Hey function, you exist?"

  • 加上括号(并且需要 parameters/variables),你是说,"Hey function, do something!"

现在开始第二部分。

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 值是多少?没有 return 语句,所以默认 some_decorator returns None

因此,my_decorator_function被创建,运行,现在有了新的价值。

我们为什么要这种行为?

当我们想要改变输出时,运行多次使用相同的输入使用相同的函数。

例如,也许我想要一个函数,每隔一次调用一次 returns "Go Left",或者每调用 5 次函数一次 "Go Right"。

如果我想用一个有多个变量的函数来做这件事,那很简单!只需将其传入并检查 if num_times == whatever_int.

但生活并不那么轻松 - 有时其他人已经编写了更简单的函数,并且只允许一个变量,因为它更通用。或者它可能太复杂了,我们需要很长时间才能弄清楚这个函数是如何工作的(而且我们通常不想违反抽象障碍)。在这些情况下,我们需要调整它们的功能以适应我们的需要。

我鼓励您阅读更多关于 Currying 的内容,因为这也将帮助您了解其他用途。