Найти тему
Калинкин

Понимание декораторов на Python

Оглавление
Взгляд на семантический поток декораторов в Python.
Взгляд на семантический поток декораторов в Python.

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

Семантически говоря, декоратор вызывается для замены существующего определения метода совершенно новым методом.

Как мы увидим в этой статье, декораторы могут быть хорошим инструментом для использования при работе со старыми кодовыми базами, которые нам нужно адаптировать к новым потребностям, свежим требованиям и т.д. Это особенно важно, когда мы работаем с принципом единой ответственности и принципом "Открыто-закрыто".

Что такое декораторы в 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 - это вызываемый объект, который

  1. Принимает исходный метод в качестве параметра.
  2. Возвращает новый метод, который заменил бы предыдущий (при этом все еще привязанный к имени исходного метода)

Сначала давайте определим функцию, которая принимает другую функцию в качестве параметра.

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

  1. Повторное использование кода невозможно: Декораторы могли бы служить адаптером, изменяя определенные параметры для соответствия определенным методам.
  2. Ведение журнала, аналитика, мониторинг: Мы могли бы использовать декораторы для отслеживания того, сколько времени требуется для выполнения функции, и регистрировать это в аналитических целях.
  3. Избегание повторений: Принцип DRY (не повторяйся) - это тот принцип, который довольно хорошо подходит для декораторов. Как только вы обнаружите, что пишете один и тот же фрагмент кода в нескольких функциях, вам следует подумать о том, чтобы инкапсулировать это в декоратор.

В заключение, декораторы относятся к функциям так же, как инкапсуляция относится к классам. Мы можем использовать декораторов, чтобы упростить наши функции и сделать их более многоразовыми.

Спасибо!