Декораторы в Python

Декораторы в Python
На чтение
19 мин.
Просмотров
23
Дата обновления
09.03.2025
Старт:14.12.2024
Срок обучения:540 ч.
«Спортивная диетология и нутрициология»
Дистанционное обучение по программе Спортивная диетология и нутрициология (540 часов) в ЦАППКК. ✍ Мы подберем вам подходящий курс, пишите!
43 000 ₽
Подробнее

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

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

Рассмотрим пример: предположим, у вас есть функция для работы с JSON, которая может вызывать исключение JSONDecodeError. Вместо того, чтобы обрабатывать это исключение в каждой функции, используйте декоратор. Декоратор позволит обрабатывать ошибку единообразно для всех функций, к которым он применён. Это повысит надёжность и качество кода.

Что такое декоратор и зачем он нужен?

Зачем это нужно? Для изменения поведения исходной функции без необходимости её прямого изменения.

  • Пример 1: Добавление логирования.

Представьте функцию, которая что-то вычисляет:

python

def вычисление(x, y):

z = x + y

print(f"Вычисляем: {x} + {y} = {z}")

return z

Но вам нужно следить за всеми вызовами. Декоратор решает эту задачу:

python

import functools

def логгирование(функция):

@functools.wraps(функция)

def обертка(*args, **kwargs):

print(f"Вызов функции {функция.__name__} с аргументами {args}, {kwargs}")

значение = функция(*args, **kwargs)

print(f"Результат: {значение}")

return значение

return обертка

@логгирование

def вычисление(x, y):

z = x + y

return z

вычисление(5, 3)

Декоратор логгирование добавляет логирование в функцию вычисление, не трогая её код!

  • Пример 2: Добавление проверки параметров.
  • Пример 3: Изменение поведения функций с помощью дополнительных аргументов.
  • Пример 4: Добавление кеширования результатов вычислений.
  • Пример 5: Автоматическое закрытие ресурсов (файлов, соединений).

С помощью декораторов вы можете добавлять новые возможности к функциям чисто как вводные переменные или поведение в вызове функции без изменения самой функции.

Как создать простой декоратор в Python?

Для создания простого декоратора в Python используйте синтаксис, основанный на функции-обёртке:

def мой_декоратор(функция): def обёртка(): print("Вызов функции до выполнения") функция() print("Вызов функции после выполнения") return обёртка

Затем, для применения декоратора к функции, используйте синтаксис:

@мой_декоратор def моя_функция(): print("Привет, мир!")

Звонок моя_функция() теперь выведет:

Вызов функции до выполнения Привет, мир! Вызов функции после выполнения

Ключевой момент: мой_декоратор принимает функцию в качестве аргумента и возвращает изменённую обёртку. Обёртка выполняет дополнительные действия до и после вызова исходной функции.

Более сложные декораторы могут принимать аргументы, возвращать значения и содержать большую логику, но базовая структура остаётся такой же.

Работа с аргументами и возвращаемыми значениями в декораторах.

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

Задача Решение Пример
Передача аргументов декорируемой функции Используйте *args и **kwargs внутри декоратора.
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Аргументы:", args, kwargs)
result = func(*args, **kwargs)
print("Результат:", result)
return result
return wrapper
@my_decorator
def greet(name, age):
return f"Привет, {name}! Тебе {age} лет."
greet("Вася", 30)
Изменение возвращаемого значения Возвращайте результат из внутренней функции wrapper.
def my_decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result * 2  # Изменяем возврат
return wrapper
@my_decorator
def square(x):
return x * x
Обработка исключений Используйте try-except внутри декоратора.
import time
def time_it(func):
def wrapper(*args, **kwargs):
try:
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Время выполнения: {end_time - start_time:.4f} секунд")
return result
except Exception as e:
print(f"Ошибка: {e}")
return None  # Обрабатываем ошибку
@time_it
def slow_function(n):
result = 0
for i in range(n):
result += i
return result
slow_function(1000000)

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

Использование декораторов с классами и методами.

Декораторы применяются к методам класса, изменяя их поведение без непосредственного изменения кода метода. Вот практический пример:

Представьте класс Calculator. Хотите добавить логирование для всех методов:

import logging
def log_method_calls(func):
def wrapper(*args, **kwargs):
logging.info(f"Вызов метода {func.__name__} с аргументами: {args}, {kwargs}")
result = func(*args, **kwargs)
logging.info(f"Результат метода {func.__name__}: {result}")
return result
return wrapper
class Calculator:
@log_method_calls
def add(self, a, b):
return a + b
@log_method_calls
def subtract(self, a, b):
return a - b
#Настройка логирования
logging.basicConfig(level=logging.INFO)
calc = Calculator()
result = calc.add(5, 3)
result = calc.subtract(10, 4)

Декоратор log_method_calls логгирует вызовы методов и результаты. Обратите внимание на использование @log_method_calls над методами add и subtract. Это ключевое место для применения.

Для классов, декоратор применяется аналогично. Он позволяет выполнять действия до и после вызова методов класса или настраивать доступ к атрибутам.

Практические примеры применения декораторов.

Для более эффективного логирования вызовов функций используйте декоратор:

import datetime
def log_function_call(func):
def wrapper(*args, **kwargs):
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"{timestamp} Вызов функции {func.__name__} с аргументами: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"{timestamp} Результат: {result}")
return result
return wrapper
@log_function_call
def my_function(a, b):
return a + b
my_function(5, 3)

Декоратор для ограничения доступа к функции:

import functools
def restricted_access(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'admin' in args[0] and args[0].get('admin'):
return func(*args, **kwargs)
else:
print("Доступ запрещен.")
return None
return wrapper
@restricted_access
def sensitive_function(data, admin=False):
print("Доступ получен. Данные:");
print(data)
return data
sensitive_function({'a': 1}, admin = True)

Декоратор, кеширующий результаты функции:

import functools
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@memoize
def expensive_calculation(x):
print(f"Вычисление для {x}...")
return x * x
print(expensive_calculation(5))
print(expensive_calculation(5))
  • Первый пример показывает логирование вызовов функций с точным временем.
  • Второй пример демонстрирует ограничение доступа по параметру admin.
  • Третий пример иллюстрирует кеширование результатов для оптимизации, избегая повторяющихся вычислений.

Используйте декораторы для повышения читаемости, повторного использования кода и безопасного управления функциями.

Когда стоит и не стоит использовать декораторы.

Стоит использовать декораторы, когда нужно обернуть функцию дополнительной логикой, не изменяя её исходный код. Например, для логгирования, измерения времени выполнения или аутентификации.

Пример: Декоратор для добавления логирования перед вызовом функции:

import time
def log_execution_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Функция {func.__name__} выполнилась за {end_time - start_time:.4f} секунд")
return result
return wrapper
@log_execution_time
def my_function(n):
time.sleep(n)
return 'Результат!'

Здесь декоратор log_execution_time добавляет измерение времени без изменения my_function.

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

Пример: Использование декоратора для простых проверок:

def check_age(func):
def wrapper(age):
if age < 18:
return "Несовершеннолетний"
return func(age)
return wrapper
@check_age
def my_function(age):
return "Вам " + str(age) + " лет"

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

Ключевой фактор: Декораторы полезны для рекурсивных или частых операций. Но они не являются панацеей для всех случаев.

Вопрос-ответ:

Как декоратор передает информацию функции?

Декоратор передает информацию функции через оберточную функцию. Внутри обёртки вы можете получить доступ к информации о функциях или ее атрибутам, а также к аргументам и результату выполнения функции и использовать её. Сама функция-декоратор получает исходную функцию как аргумент (передается как аргумент в inner-функцию обёртки).

Когда использовать декораторы?

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

0 Комментариев
Комментариев на модерации: 0
Оставьте комментарий

Курсы