@lutokris

Как вообще понимать декораторы в Python?

Уже 3 дня сижу.. хоть убей, вообще не понимаю. Сижу пишу код, работает, но не понимаю - как? откуда и что, почему? Допустим есть такой пример:
def uppercase(func):
    def  wrapper():
        original_result = func()
        return f'Большие {original_result.upper()}'
    return wrapper

@uppercase
def greet():
    return 'маленькие буквы'

print(greet())
>>>Большие МАЛЕНЬКИЕ БУКВЫ

Если я правильно понимаю - функция uppercase принимает в себя другую функцию(точнее объект). Внутри нее описана другая функция с названием wrapper, внутри которой делают
строковые операции с результатом выполнения другой функции. Дальше у меня возник вопрос и ступор - мы почему то возвращаем не результат выполнения функции wrapper, а возвращаем саму функцию-объект. И она сама каким-то образом вызывается и
выполняется, хоть мы и явно не пишем это. Если я правильно понимаю когда мы декорируем функцию uppercase и пишем def greet, мы типа просто создаем функцию uppercase(greet) которая будет всегда вызываться когда мы пишем greet()? И почему тогда если uppercase(greet) возвращает объект типа функция, как она сама собой вызывается? В общем с этими декораторами у меня полный ступор.
  • Вопрос задан
  • 162 просмотра
Решения вопроса 1
@o5a
Дальше у меня возник вопрос и ступор - мы почему то возвращаем не результат выполнения функции wrapper, а возвращаем саму функцию-объект.

Так и есть. Нам же не нужно в самом декораторе вызывать функцию, а только указать, какую функцию мы используем. Без скобок () это просто ссылка на сам объект (функцию).
Пример:
def f1(): return 1

a = f1() # присваиваем результат работы функции, т.е. в a будет 1
a = f1 # присваиваем саму функцию
# тогда a - это не результат работы функции f1, а сама функция f1
# и мы можем ее запустить
print(a() ) # выведет 1, т.к. мы через a запустили f1

Декораторы так и делают. Создают функцию и возвращают ее, но не запускают. А запускается эта "обертка" во время запуска нашей основной функции.
Т.е. когда добавляется декоратор
@uppercase
def greet(): ...
# то когда мы запускаем функцию
greet()
# по факту запускается
uppercase(greet)()

Но что возвращает uppercase(greet) ? Смотрим по коду. А возвращает она некий wrapper. Что делает wrapper?
original_result = func() # запускает переданную ему функцию на исполнение, в нашем случае greet
return f'Большие {original_result.upper()}' # и возвращает строку с результатами исполнения

И она сама каким-то образом вызывается и выполняется, хоть мы и явно не пишем это.

Исполняется потому, что запуск декорированной функции означает запуск
uppercase(greet)()
а т.к. uppercase(greet) возвращает wrapper
то uppercase(greet)() запускает wrapper()
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
@Drill
мы типа просто создаем функцию uppercase(greet) которая будет всегда вызываться когда мы пишем greet()? И почему тогда если uppercase(greet) возвращает объект типа функция, как она сама собой вызывается?


@uppercase
def greet():
    return 'маленькие буквы'


равносильно

def greet():
    return 'маленькие буквы'

greet = uppercase(greet)


В чем суть декораторов?
Ответ написан
Комментировать
Подробно о технической реализации можно почитать в этой статье https://habr.com/ru/post/141411/

Вкратце, происходит следующее - декоратор задает ссылку greet = wrapper, поэтому, когда происходит вызов greet(), вместо этого вызывается wrapper(), а не определенный в изначальном коде greet.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы