На этом шаге мы рассмотрим, как это реализуется.
Теперь, когда вы чуть ближе познакомились с синтаксисом декораторов, давайте напишем еще один декоратор, который действительно что-то делает и изменяет поведение декорированной функции.
Вот чуть более сложный декоратор, который преобразовывает результат декорированной функции в буквы верхнего регистра:
>>> def uppercase(func): def wrapper(): original_result = func() modified_result = original_result.upper() return modified_result return wrapper
Вместо того чтобы просто возвратить входную функцию, как это делал пустой декоратор, декоратор uppercase() на лету определяет новую функцию (замыкание) и использует ее в качестве обертки входной функции, чтобы изменить ее поведение во время вызова.
Замыкание wrapper() имеет доступ к недекорированной входной функции, и оно свободно может выполнить дополнительный программный код до и после ее вызова. (Технически замыканию вообще не нужно вызывать входную функцию.)
Заметьте, что вплоть до настоящего момента декорированная функция ни разу не была исполнена. На самом деле, в вызове входной функции на данном этапе нет никакого смысла - потребность в том, чтобы декоратор был в состоянии изменить поведение своей входной функции, возникнет, только когда он наконец будет вызван.
Самое время, чтобы взглянуть на декоратор uppercase() в действии. Что произойдет, если продекорировать им оригинальную функцию greet()?
>>> @uppercase def greet(): return 'Привет!' >>> greet() 'ПРИВЕТ!'
Надеюсь, вы ждали именно этот результат. Давайте взглянем поближе на то, что здесь только что произошло. В отличие от null_decorator(), декоратор uppercase() при декорировании функции возвращает другой объект-функцию:
>>> greet <function greet at 0x10e9f0950> >>> null_decorator(greet) <function greet at 0x10e9f0950> >>> uppercase(greet) <function uppercase.<locals>.wrapper at 0x76da02f28>
И как вы видели чуть раньше, ему это нужно, чтобы изменить поведение декорированной функции, когда он в итоге будет вызван. Декоратор uppercase() сам является функцией. И единственный способ повлиять на "будущее поведение" входной функции, которую он декорирует, состоит в том, чтобы подменить (или обернуть) входную функцию замыканием.
Вот почему декоратор uppercase() определяет и возвращает еще одну функцию (замыкание), которая затем может быть вызвана в дальнейшем, выполняет оригинальную входную функцию и модифицирует ее результат.
Декораторы изменяют поведение вызываемого объекта посредством обертки-замыкания, в результате чего вам не приходится необратимо модифицировать оригинал. Оригинальный вызываемый объект не изменяется необратимо - его поведение меняется, только когда он декорирован.
Это позволяет прикреплять к существующим функциям и классам конструктивные блоки многократного использования, в частности функционал журналирования и другое инструментальное оформление. Этот факт делает декораторы настолько мощным функциональным средством языка, что они нередко используются в стандартной библиотеке Python и в сторонних пакетах.
На следующем шаге мы рассмотрим применение многочисленных декораторов к функции.