ром (syntactic sugar) и краткой формой для этого широко применяемого
шаблона.
Обратите внимание: синтаксис
@
декорирует функцию непосредственно
во время ее определения. При этом становится трудно получить доступ
к недекорированному оригиналу без хрупких хакерских фокусов. По этой
причине вы можете решить вручную декорировать некоторые функции
для сохранения способности вызвать и недекорированную функцию.
84 Глава 3 • Эффективные функции
Декораторы могут менять поведение
Теперь, когда вы чуть ближе познакомились с синтаксисом декораторов,
давайте напишем еще один декоратор, который действительно что-то
делает и изменяет поведение декорированной функции.
Вот чуть более сложный декоратор, который преобразовывает результат
декорированной функции в буквы верхнего регистра:
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():
3 .3 . Сила декораторов 85
return 'Привет!'
>>> greet()
'ПРИВЕТ!'
Надеюсь, вы ждали именно этот результат. Давайте взглянем поближе
на то, что здесь только что произошло. В отличие от
null_decorator
,
декоратор
uppercase
при декорировании функции возвращает другой
объект-функцию:
>>> greet
>>> null_decorator(greet)
>>> uppercase(greet)
.wrapper at 0x76da02f28>
И как вы видели чуть раньше, ему это нужно, чтобы изменить поведение
декорированной функции, когда он в итоге будет вызван. Декоратор
uppercase
сам является функцией. И единственный способ повлиять на
«будущее поведение» входной функции, которую он декорирует, состоит
в том, чтобы подменить (или обернуть) входную функцию замыканием.
Вот почему декоратор
uppercase
определяет и возвращает еще одну функ-
цию (замыкание), которая затем может быть вызвана в дальнейшем, вы-
полняет оригинальную входную функцию и модифицирует ее результат.
Декораторы изменяют поведение вызываемого объекта посредством
обертки-замыкания, в результате чего вам не приходится необратимо
модифицировать оригинал. Оригинальный вызываемый объект не из-
меняется необратимо — его поведение меняется, только когда он деко-
рирован.
Это позволяет прикреплять к существующим функциям и классам кон-
структивные блоки многократного использования, в частности функ-
ционал журналирования и другое инструментальное оформление. Этот
факт делает декораторы настолько мощным функциональным средством
языка, что они нередко используются в стандартной библиотеке Python
и в сторонних пакетах.
86 Глава 3 • Эффективные функции
Короткая пауза
Кстати, если в этом месте вы почувствовали, что вам нужен короткий
перерыв на чашечку кофе или на прогулку вокруг квартала, то это абсо-
лютно нормально. По моему мнению, в Python замыкания и декораторы
являются одними из самых трудных для понимания концепций.
Пожалуйста, не торопитесь и не переживайте по поводу того, что не полу-
чается разобраться во всем этом сразу же. Переварить услышанное часто
помогает работа с примерами. Поэкспериментируйте с ними по очереди
в сеансе интерпретатора.
Уверен, что у вас получится!
Применение многочисленных декораторов к функции
Возможно, вас не удивит, что к функции можно применять более одного
декоратора. В этом случае их эффекты накапливаются, и именно этот
факт делает декораторы столь полезными в качестве структурных блоков
многократного использования.
Приведем пример. Представленные ниже два декоратора обертывают
выходную строку декорированной функции в HTML-теги. Глядя на то,
как теги вложены, вы видите, в каком порядке Python применяет много-
численные декораторы:
def strong(func):
def wrapper():
return '' + func() + ''
return wrapper
def emphasis(func):
def wrapper():
return '' + func() + ''
return wrapper
Теперь давайте возьмем эти два декоратора и одновременно применим их
к нашей функции
greet
. Для этого вы можете использовать обычный син-
3 .3 . Сила декораторов 87
таксис
@
и просто «уложить» многочисленные декораторы вертикально
поверх одной-единственной функции:
@strong
@emphasis
def greet():
return 'Привет!'
Какой результат вы ожидаете увидеть, если выполнить декорированную
функцию? Сначала декоратор
@emphasis
добавит тег
? Или же прио-
ритет имеет тег
@strong
? Когда вы вызываете декорированную функцию,
происходит вот что:
>>> greet()
Do'stlaringiz bilan baham: |