Всем привет!
Перевод статьи подготовлен для студентов курса «Web-разработчик на Python». Интересно развиваться в данном направлении? Запишитесь на День Открытых Дверей курса и пообщайтесь вживую с преподавателем: онлайн-трансляция 23 июля в 20:00 по мск.!
Когда вы упражнялись в программировании на языке Python, вы, должно быть сталкивались с таким понятием, как декораторы. Они являются одним из самых элегантных и часто используемых инструментов в современных библиотеках и фреймворках. Декораторы — хороший способ инкапсулировать множество деталей реализации, оставляя на поверхности простой интерфейс.
Давайте рассмотрим пример простого декоратора логина, который проверяет, что пользователь вошел в систему, прежде чем давать ему возможности редактировать посты. Потом декоратор перенаправляет на страницу входа в систему или регистрации, а затем с помощью правильно заданных параметров возвращает обратно на ту же страницу после успешного прохождения идентификации. Чтобы воспользоваться данной функцией, вам нужно всего лишь написать @login_required перед целевой функцией.
@login_required
def edit_post(post_id):
...
С декораторами очень легко работать, но создание декораторов задача непростая даже для опытных Python-программистов. В этой статье мы с вами рассмотрим пошагово как работают декораторы в Python.
Функции
Функции также называются объектами первого класса в Python. Функции это такие же значения, как числа, списки и строки, как видно из следующего примера.
>>> def foo():
... return 1
...
>>>
>>> foo()
1
>>>
У функций есть свое собственное пространство имен, где происходит поиск имен переменных в первую очередь, когда они встречаются в теле функции. Давайте напишем простую функцию, чтобы понять разницу между глобальной и локальной областью видимостью.
>>>
>>> a_string = "This is a global variable"
>>>
>>> def foo():
... print(locals())
...
>>>
>>> print(globals())
{..., 'a_string': 'This is a global variable'}
>>>
>>> foo() # 2
{}
>>>
Область видимости функции как переменная
Правило области видимости в Python гласит, что при создании переменной всегда создается новая локальная переменная, но доступ к переменной определяется в локальной области видимости при поиске по всем ближайшим областям совпадений имен переменных. Это не значит, что мы не можем получить доступ к глобальным переменным из нашей функции. Чтобы выводить глобальную переменную мы изменим функцию foo следующим образом:
>>>
>>> a_string = "This is a global variable"
>>>
>>> def foo():
... print(a_string) #1
...
>>>
>>> foo()
This is a global variable
>>>
Время жизни переменной
В пространстве имен живут не только переменные, но и они имеют время жизни, о котором важно помнить. Рассмотрим пример, который иллюстрирует не только правила области видимости и те проблемы, которые они могут вызвать, но и то, как они взаимодействуют с вызовом функции и то как они работают в Python и других языках.
>>> def foo():
... x = 1
...
>>> foo()
>>>
>>> print(x) # 1
Traceback (most recent call last):
...
NameError: name 'x' is not defined
>>>
Вложенные функции
В Python можно создавать вложенные функции, а это значит, что мы можем объявлять функции внутри функций, и все правила области видимости и времени жизни по-прежнему действуют.
>>>
>>> def outer():
... x = 1
... def inner():
... print(x) # 1
... inner() # 2
...
>>> outer()
1
>>>
Декораторы
Замыкание (closure), которое принимает функцию в качестве параметра и возвращает функцию, называется декоратором. Рассмотрим пример полезных декораторов.
>>>
>>> def outer(some_func):
... def inner():
... print("before some_func")
... ret = some_func() # 1
... return ret + 1
... return inner
...
>>> def foo():
... return 1
...
>>> decorated = outer(foo) # 2
>>>
>>> decorated()
before some_func
2
>>>
Декорированная переменная представляет собой декорированную версию foo. На самом деле, мы могли бы заменить foo ее декорированной версией и не изучать новый синтаксис, просто переназначив переменную, содержащую нашу функцию:
>>>
>>> foo = outer(foo)
>>>
>>> foo # doctest: +ELLIPSIS
<function outer.<locals>.inner at 0x...>
>>>
Теперь для отслеживания вызовов функций у нас есть красивый декоратор. Декораторы могут использоваться для работы с любым языком программирования с помощью Python. Это крайне полезный инструмент, механизм работы которого необходимо понять, чтобы правильно их применять.