Python内部函数/修饰符:返回内部函数时应何时使用括号?

时间:2019-06-04 20:18:50

标签: python python-decorators

我正在学习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)。

1 个答案:

答案 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的更多信息,因为这也会帮助您了解其他用途。