Декораторы представляют собой удобный для восприятия человеком способ расширения возможностей функции, метода или класса извне. Использование декораторов особенно полезно при декорировании (т. е. расширении) похожих функций для выполнения ими одного и того же, но без добавления ненужных повторений в коде.
Вот простой пример того, как расширить функцию с помощью декоратора:
@guard_zero
def divide(x, y):
return x / y
Декоратор @guard_zero
расширяет функциональность метода divide
тем, что не допускает деления на 0
. Однако пока что guard_zero
не существует, ведь он еще не реализован. Далее покажем, как именно это сделать.
Как создать декоратор в Python
Лучший способ продемонстрировать, каковы декораторы в действии, — создать и задействовать один из них. Реализуем наш декоратор @guard_zero
.
Вначале у нас есть только этот метод, который делит два числа:
def divide(x, y):
return x / y
Проблема с этим методом в том, что нет проверки, не равно ли 0
значение y
. Очевидное решение здесь — задействовать простую проверку if
. Но есть и альтернативное решение: декораторы.
Начнем с создания простой функции-декоратора guard_zero
:
def guard_zero(operate):
def inner(x, y):
if y == 0:
print("Cannot divide by 0.")
return
return operate(x, y)
return inner
Декоратор в Python — это такая же обычная функция. Она принимает в качестве аргумента функцию operate
. Затем расширяет функциональность operate
, создавая внутреннюю функцию и добавляя туда расширенное поведение. После чего возвращает внутреннюю функцию inner
, которая становится новой версией функции operate
.
И вот декоратор guard_zero
готов. Теперь расширим (т. е. декорируем) функцию divide
:
divide = guard_zero(divide)
Выглядит прямо как добавление переменной нового значения. Но в этом случае существующей функции просто добавляется новая функциональность, что совершенно допустимо.
Есть и более распространенный синтаксис применения декоратора. Если в предыдущем синтаксисе функция заменялась новой своей версией, то здесь декоратор просто ставится перед определением функции:
@guard_zero
def divide(x, y):
return x / y
Такой синтаксис более удобен для восприятия человеком, и суть его ясна: метод divide
расширяется, чтобы исключить деление на 0
.
Теперь пора протестировать метод divide
различными входными данными и убедиться, что декоратор делает то, что должен:
print(divide(5, 0))
print(divide(5, 2))
Вывод:
Cannot divide by 0.
None
2.5
Обратите внимание на None
в выводе. А все потому, что декоратор guard_zero
возвращает None
, когда значение y
равно 0
.
Вот и все! Теперь вы знаете, что такое декораторы и как их использовать. А вот и весь код:
def guard_zero(operate):
def inner(x, y):
if y == 0:
print("Cannot divide by 0.")
return
return operate(x, y)
return inner
@guard_zero
def divide(x, y):
return x / y
print(divide(5, 0)) # выводит Cannot divide by 0 («На 0 делить нельзя»)
Напомним: метод декоратора guard_zero
принимает в качестве аргумента функцию divide
и создает ее расширенную версию.
Когда же используются декораторы?
«К чему такие сложности?» — скажете вы. Достаточно написать простую проверку if
внутри функции divide
, сэкономив при этом несколько строк кода.
Мощь декораторов становится очевидной, когда видишь, что они помогают избежать повторения кода.
Представьте, что у вас десять подобных друг другу методов. Необходимо убедиться, что второй параметр каждого из них не равен 0
. В этой ситуации вы:
- либо потратите время на написание десяти одинаковых проверок
if
для каждого метода отдельно, - либо создадите декоратор и напишете
@zero-guard
перед каждой функцией.
Последний подход будет более предпочтительным, ведь в этом случае нужно просто написать декоратор в одном месте и добавить везде @zero-guard
.
Заключение
В Python декораторы используются для расширения функциональных возможностей функции, метода или класса извне.
Например, чтобы не допустить деления на 0
, задействуется декоратор guard_zero
. Просто добавляем его перед определением метода и таким образом используем этот декоратор в любом месте кода:
@guard_zero
def divide(x, y):
return x / y
Но когда надо задействовать декораторы? Главным образом когда нужно избежать повторений при использовании похожих методов. Вместо того, чтобы вносить одно и то же изменение в несколько методов, куда как проще сделать одно изменение, а затем декорировать им все методы.
Спасибо за внимание! Надеюсь, статья была вам полезна.
Читайте также:
- 3 признака того, что ваш ИИ-проект обречен
- Компилятор VS интерпретатор: ключевые отличия
- Как конвертировать PDF-файлы в PNG с помощью Python
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Artturi Jalli: Understand Python Decorators in 3 Minutes