Почему я должен вернуться снова внутри функции более высокого порядка - PullRequest
0 голосов
/ 23 февраля 2019

Рассматривал некоторые decorators в python3.Ниже приведен фрагмент кода.Почему я должен возвращать функцию (fn), когда она вызывается внутри функции wrapper

from functools import wraps

def log_function_data(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        print(fn.__name__)
        print(fn.__doc__)
        return fn(*args, **kwargs)   #why am i returning this?
    return wrapper

@log_function_data
def add(x,y):
    '''Adds 2 numbers and returns'''
    return x + y

Функция add уже возвращает результат операции.Поэтому я вызываю функцию добавления без decorator, возвращаемое работает так:

def add(x,y):
    '''Adds 2 numbers and returns'''
    return x + y

result = add(2,3) ##have the result = 5

Ответы [ 2 ]

0 голосов
/ 24 февраля 2019

Представьте, что вы знаете кого-то, кто очень хорош в математике.Представьте, что эта девушка, Алиса , настолько хороша, что она - единственный человек в мире, который может решить проблему X .

Здесь есть одна загвоздка: Алиса говорит только по-французски, но проблема X объясняется на любом случайно выбранном языке, и ожидается, что на этом же языке будет дан ответ.К сожалению, у нее нет возможности интерпретировать проблему X на любом другом языке, кроме французского.

Представьте, что вы заинтересованы в решении проблемы X. Вы думаете об этом 2 минуты и приходите к решению:

Я знаю Алису, у нее математическая часть, и я знаю Бернарда, он может переводить с любого языка на французский и обратно в совершенстве.Давайте заставим их работать вместе для решения проблемы X!

Алгоритм, который они будут использовать для работы, следующий:

  1. Бернард читает проблему X и переводит ее на французский
  2. Алиса решает французскую версию X
  3. Бернард переводит ответ Алисы обратно на исходный язык
  4. Возвращает решение

В python это выглядит какэто:

def main():

  insane_translator = Bernard()

  def attach_translator(french_solver):

      def translation_decorator(X):
          french_X = insane_translator.translate(X, input_lang=X.lang, output="fr")
          french_solution = french_solver(french_X)
          solution = insane_translator.translate(french_solution, input_lang="fr", output=X.lang)
          return solution

      return translation_decorator

  x = X()
  solve = attach_translator(Alice.solve)
  solution = solve(x)
  print(solution)

class Alice:
    @staticmethod
    def solve(french_X):
        # Do some incredible things using french_X, Alice is a genius.
        return french_X + " 42"

class Bernard:
    def translate(self, text, input_lang=None, output=None):
        # Translate from any input language to any output language, Bernard is insane!
        return f"from {input_lang} to {output} of: \n\t{text}"

class X:
  def __init__(self):
    self.lang = "Alien"

  def __str__(self):
    return "\tProblem definition"


main()

Бернард используется в качестве переводчика для «украшения» рабочей силы Алисы.Если вы не звоните по номеру fn, это означает, что вы не просите Алису работать, поэтому вы переводите только туда и обратно без пользы.Цель декоратора состоит в том, чтобы добавить некоторую обработку до и после вызова декорированной функции. Весь смысл в том, чтобы вызвать функцию не так, как было изначально задумано.

0 голосов
/ 24 февраля 2019
    return fn(*args, **kwargs)  # why am i returning this?

Пожалуйста, обратитесь к документации .Python интерпретирует ваш код в разное время:

  1. один раз, в import время (когда мы генерируем байт-код для функций)
  2. несколько раз, во время выполнения (при вызове функции)

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

Чтобы лучше понять оформление, вам может показаться полезным поиграть с dis .Я рекомендую сделать однострочное изменение в вашем исходном коде и обратить внимание на различия между байт-кодами вариантов.

...