Декораторы - это очень широко известный шаблон проектирования в программной инженерии, в котором поведение отдельных методов и классов изменяется с помощью функции переноса.
Семантически говоря, декоратор вызывается для замены существующего определения метода совершенно новым методом.
Как мы увидим в этой статье, декораторы могут быть хорошим инструментом для использования при работе со старыми кодовыми базами, которые нам нужно адаптировать к новым потребностям, свежим требованиям и т.д. Это особенно важно, когда мы работаем с принципом единой ответственности и принципом "Открыто-закрыто".
Что такое декораторы в Python?
Декоратор в Python - это любой вызываемый объект, который используется для изменения поведения любого метода или класса. Возьмем, к примеру, этот устаревший метод в нашем проекте:
def greet_someone(name: str):
return f"Hello and welcome, {name}!"
Теперь, допустим, мы хотим добавить переводы в наш проект, и мы хотим, чтобы этот метод, в частности, отображал приветствие на указанном языке, мы могли бы сделать это простым добавлением декоратора.
@translate
def greet_someone(name: str):
return f"Hello and welcome, {name}!"
Как написать декоратора на Python
Давайте закодируем translate decorator.
По определению, мы знаем, что декоратор в Python - это вызываемый объект, который
- Принимает исходный метод в качестве параметра.
- Возвращает новый метод, который заменил бы предыдущий (при этом все еще привязанный к имени исходного метода)
Сначала давайте определим функцию, которая принимает другую функцию в качестве параметра.
def translate(original_func):
...
Далее мы собираемся заставить translate decorator возвращать новый метод.
def translate(original_func):
# аргументы, kwargs - это параметры original_func
def replacement_function(*args, **kwargs):
return "This is from the new function"
return replacement_function
Теперь давайте посмотрим, как наш метод оформления @translate сочетается с другим оригинальным методом.
def greet_someone(name: str):
return f"Здравствуйте и добро пожаловать, {name}!"
print(greet_someone("Tom"))
# "Привет и добро пожаловать, Том!"
@translate
def greet_someone(name: str):
return f"Здравствуйте и добро пожаловать, {name}!"
print(greet_someone("Alex"))
# "Это, если из новой функции"
Теперь, когда мы увидели нашего декоратора в работе, давайте модифицируем декоратора, чтобы он был настоящим переводчиком
В этом учебном примере мы используем библиотеку под названием googletrans. Итак, давайте приступим к установке библиотеки:
pip install googletrans
# script.py
from googletrans import Translator
translator = Translator()
APP_LANGUAGE = 'fr'
def translate(func):
def replacement_func(*args, **kwargs):
result = func(*args, **kwargs)
return translator.translate(result, dest=APP_LANGUAGE).text
return replacement_func
@translate
def greet_someone(name: str):
return f"Hello and welcome, {name}!"
print(greet_someone("Pierre"))
Мы видим следующий результат, правильно переведенный на французский язык приложения.
Bonjour et bienvenue Pierre !
Теперь у нас есть удобный способ преобразовать все функции, возвращающие тексты в нашем проекте, на собственный язык приложения, просто добавив всего одну строку кода.
Кто-то может задаться вопросом, что, если я захочу передать параметр самому декоратору? Сделать его более гибким и перевести текст не только на “французский”.
Передача параметров декораторам
Чтобы декоратор мог принимать параметры в Python, нам пришлось бы обернуть наш декоратор внутри другой функции, которая принимала бы эти параметры.
Давайте вернемся назад и изменим наш декоратор @translate, чтобы использовать языковой параметр, а затем переведем наш текст на указанный язык.
# script_2.py
from googletrans import Translator
translator = Translator()
APP_LANGUAGE = 'fr'
def translate(output_language):
def inner(func):
def replacement_func(*args, **kwargs):
result = func(*args, **kwargs)
return translator.translate(result, dest=output_language).text
return replacement_func
return inner
@translate("pt")
def greet_someone(name: str):
return f"Hello and welcome, {name}!"
print(greet_someone("Pierre"))
Теперь мы получаем выходные данные нашей функции, переведенные на язык по нашему выбору и спецификации — в данном случае португальский “pt”.
Olá e bem-vindo, Pierre!
Вложенность декораторов
Декораторы могут быть вложенными. Когда декораторы вложены, результат может быть выражен в виде переноса функций в порядке от выхода к входу, сверху вниз.
Другими словами,
@first_decorator
@second_decorator
@third_decorator
def my_function():
print("Hello")
Может быть выражено как
first_decorator(second_decorator(third_decorator(my_function)))
Примеры использования декораторов в Python
- Повторное использование кода невозможно: Декораторы могли бы служить адаптером, изменяя определенные параметры для соответствия определенным методам.
- Ведение журнала, аналитика, мониторинг: Мы могли бы использовать декораторы для отслеживания того, сколько времени требуется для выполнения функции, и регистрировать это в аналитических целях.
- Избегание повторений: Принцип DRY (не повторяйся) - это тот принцип, который довольно хорошо подходит для декораторов. Как только вы обнаружите, что пишете один и тот же фрагмент кода в нескольких функциях, вам следует подумать о том, чтобы инкапсулировать это в декоратор.
В заключение, декораторы относятся к функциям так же, как инкапсуляция относится к классам. Мы можем использовать декораторов, чтобы упростить наши функции и сделать их более многоразовыми.
Спасибо!