Шаг 46.
Python: тонкости программирования.
Эффективные функции. Сила декораторов. Декораторы могут менять поведение

    На этом шаге мы рассмотрим, как это реализуется.

    Теперь, когда вы чуть ближе познакомились с синтаксисом декораторов, давайте напишем еще один декоратор, который действительно что-то делает и изменяет поведение декорированной функции.

    Вот чуть более сложный декоратор, который преобразовывает результат декорированной функции в буквы верхнего регистра:

>>> 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 и в сторонних пакетах.

    На следующем шаге мы рассмотрим применение многочисленных декораторов к функции.




Предыдущий шаг Содержание Следующий шаг