Внутренние функции / декораторы Python: Когда мне следует использовать круглые скобки при возврате внутренней функции? - PullRequest
1 голос
/ 04 июня 2019

Я узнаю о Python-декораторах и внутренних функциях и у меня есть несколько вопросов об уроке, который я изучаю с помощью видео на YouTube с codeacademy.com https://youtu.be/WOHsHaaJ8VQ.

При использовании внутренних функций иногда приходится возвращать функцию с круглыми скобками, а иногда без.

Если я вызываю внутреннюю функцию без использования декораторов, я должен использовать скобки при возврате внутренней функции, в противном случае кажется, что внутренняя функция возвращается как объект (?). В видео YouTube с codeacademy.com, а также в этом 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 Ответ

2 голосов
/ 05 июня 2019

Давайте разберем это на две части.

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» каждый раз, когда она вызывается, или «Go Right» каждые 5 раз, когда вызывается функция.

Если я хочу сделать это с функцией с более чем одной переменной, это легко!Просто передайте его и проверьте if num_times == whatever_int.

Но жизнь не так проста - иногда у других людей уже написаны функции, которые намного проще и допускают только одну переменную, потому что она более обобщаема.Или, может быть, это настолько сложно, что нам понадобится очень много времени, чтобы понять, как работает эта функция (и мы обычно не хотим нарушать барьеры абстракции).В этих ситуациях нам необходимо адаптировать их функции к нашим потребностям.

Я бы посоветовал вам прочитать больше о Curry , поскольку это поможет вам понять и другие области применения.

...