• Добро пожаловать на сайт - Forumteam.bet !

    Что бы просматривать темы форума необходимо зарегестрироваться или войти в свой аккаунт.

    Группа в телеграме (подпишитесь, что бы не потерять нас) - ForumTeam Chat [Подписатся]
    Связь с администратором - @ftmadmin

Декораторы для самых маленьких (Python)

Article Publisher

Публикатор
Команда форума
Регистрация
05.02.25
1739805813297.png


Хочу написать серию статей для начинающих разработчиков. Посты будут трех уровней сложности (от меньшего к большему) на разные аспекты языка с которыми сложно справиться обывателю.

Не хочу лить воды, уверен, применение вы найдете сразу, потому что во-первых декораторы - это красивый синтаксический сахар, во вторых очень мощный прием для решения многих классов задач. В этой статье будет код с краткими разъяснениями и ничего более.

Я прикреплю ссылки на смежные статьи (тоже хороший материал, но ИМХО в них либо странная подача либо некоторая неполнота), мне кажется, что мой материал в разрезе 3 статей на каждую тему будет лаконичней и полней, но тут уже решать только вам, дорогие читатели

Следующая часть для тех, кто знает базу

Не будем повторяться​

Да, начнем со смежных статей, не хочу тратить ваше время, если вам не нравится подача и / или посыл

Отправляемся к коду​

Базовый принцип, на котором основана работа декораторов - замыкание
Простыми словами, мы просто "Запоминаем" значение переменных в каком-то контексте (пространстве имен)

# Минимальный пример замыкания
def summ(a):
# Объявляем функцию внутри другой и используем значение
# переменной из пространства имен объемлющей функции
return lambda b: a + b


# "Запоминаем" 10. При дальнейших вызовах a_plus_b
# результатом будет 10 + переданное значение
a_plus_b = summ(10)
# Прибавляем 5
a_plus_b(5)
# Out[8]: 15

Переходим к декораторам​

Базово декоратор - это объемлющая функция и оберточная. Объемлющая функция декорирует целевую функцию оберточной функцией, а оберточная функция определяет поведение вызова итогового объекта. Сложно и не интересно, согласен, поэтому пример

# Минимальный пример декоратора
def regular_decorator(function):
print("regular_decorator wrap")
# Объявляем функцию внутри функции декоратора, которая принимает любые
# позиционные и именованные аргументы это необходимо для того,
# чтобы обернуть декорируемую функцию и иметь возможность прокинуть
# произвольный набор аргументов в декорируемую функцию
@wraps(function) # Данная строка не обязательна
def wrapper(*args, **kwargs):
# Выполняем какой-то код до вызова декорируемой функции
print("before regular_decorated call")
# Получаем результат выполнения декорируемой функции
result = function(*args, **kwargs)
# Выполняем какой-то код после вызова декорируемой функции
print("after regular_decorated call")
# Возвращаем результат выполнения функции
return result

# Возвращаем оберточную функцию
return wrapper


# Декорируем целевую функцию
@regular_decorator
def regular_decorated():
print("regular_decorated call")
# Здесь консоль выведет regular_decorator wrap

# Вызываем декорируемую функцию
regular_decorated()
# Здесь в консоль выводится следующее
# before regular_decorated call
# regular_decorated call
# after regular_decorated call

Декораторы бывают сложнее​

Периодически (а иногда и часто), у вас появляется желание / необходимость делать ваши декораторы гибкими или настраиваемыми. Знакомьтесь с параметризируемыми декораторами.

# Минимальный пример декоратора, принимающего параметры
def parametrized_decorator(
target: Callable | None = None,
/, # Указываем, что параметр target может передаться
# исключительно как позиционный
multiply_result: float = 2.0,
add_to_result: float = 0.0,
):
print("parametrized_decorator parametrize")
# Объявляем функцию декоратор
def decorator(function):
print("regular_decorator wrap")
# Объявляем оберточную функцию
@wraps(function) # Данная строка не обязательна
def wrapper(*args, **kwargs):
print("before parametrized_decorator call")
result = function(*args, **kwargs)
print("after parametrized_decorator call")
# Увеличиваем и умножаем результат на значения их параметров
return (result + add_to_result) * multiply_result

# Возвращаем оберточную функцию
return wrapper

# Если декоратор используется без параметров и первым аргументом является
# вызываемый объект, то сразу же оборачиваем target функцией декоратором
if isinstance(target, Callable):
return decorator(target)
# Если первый аргумент не передан, возвращаем функцию декоратор
return decorator


# Декорируем целевую функцию без параметров
# в данном случае isinstance(target, Callable) is True
@parametrized_decorator
def default_parametrized_decorated(value):
print("default_parametrized_decorated call")
return value
# Здесь консоль выведет
# parametrized_decorator parametrize
# regular_decorator wrap

# Вместо использования @parametrized_decorator вы также можете сделать следующее
parametrized_decorator(default_parametrized_decorated)
parametrized_decorator()(default_parametrized_decorated)

# Вызываем декорируемую функцию
default_parametrized_decorated(1)
# Консоль выведет следующее
# before parametrized_decorator call
# default_parametrized_decorated call
# after parametrized_decorator call
# Out[19]: 2.0

# Декорируем целевую функцию c параметром
# в данном случае isinstance(target, Callable) is False
@parametrized_decorator(multiply_result=4)
def parametrized_decorated_with_multiple_result(value):
print("parametrized_decorated_with_multiple_result call")
return value
# Здесь консоль выведет
# parametrized_decorator parametrize
# regular_decorator wrap

# Вызываем декорируемую функцию
parametrized_decorated_with_multiple_result(1)
# Консоль выведет следующее
# before parametrized_decorator call
# parametrized_decorated_with_multiple_result call
# after parametrized_decorator call
# Out[21]: 4

Декоратор - это не одинокая структура​

Да, на функцию можно вешать любое количество декораторов (порядок важен)

# Также вы можете использовать декораторы вместе, например
@parametrized_decorator(add_to_result=4)
@parametrized_decorator(multiply_result=2)
def double_parametrized_decorated_with_multiple_result(value):
print("double_parametrized_decorated_with_multiple_result call")
return value
# Здесь консоль выведет
# parametrized_decorator parametrize
# parametrized_decorator parametrize
# regular_decorator wrap
# regular_decorator wrap

# Вызываем декорируемую функцию
double_parametrized_decorated_with_multiple_result(1)
# before parametrized_decorator call
# before parametrized_decorator call
# double_parametrized_decorated_with_multiple_result call
# after parametrized_decorator call
# after parametrized_decorator call
# Out[21]: 12.0

# Да, порядок имеет значение
@parametrized_decorator(multiply_result=2)
@parametrized_decorator(add_to_result=4)
def double_parametrized_decorated_with_multiple_result(value):
print("double_parametrized_decorated_with_multiple_result call")
return value

# Вызываем декорируемую функцию
double_parametrized_decorated_with_multiple_result(1)
# before parametrized_decorator call
# before parametrized_decorator call
# double_parametrized_decorated_with_multiple_result call
# after parametrized_decorator call
# after parametrized_decorator call
# Out[24]: 20.0

Заключение​

В данной статье мы узнали о существовании паттерна "замыкание", узнали о существовании декораторов и увидели несколько простых примеров
 
Сверху Снизу