Декораторы в Python – это мощный инструмент для добавления функциональности к существующим функциям или методам без изменения их исходного кода. Например, если вам нужно замерить время выполнения функции, достаточно создать декоратор @timer, который автоматически добавит эту возможность. Это упрощает поддержку кода и делает его более читаемым.
Рассмотрим пример: декоратор @cache может значительно ускорить выполнение функций, которые часто вызываются с одинаковыми аргументами. Вместо повторных вычислений результаты сохраняются в памяти, что особенно полезно для рекурсивных алгоритмов или функций с высокой вычислительной сложностью. Такой подход экономит ресурсы и улучшает производительность.
Ещё один полезный пример – декоратор @validate_input, который проверяет корректность входных данных перед выполнением функции. Это помогает избежать ошибок и упрощает отладку. Например, вы можете автоматически проверять, что аргументы являются числами или строками, и генерировать исключения, если это не так.
Декораторы также позволяют реализовать логирование, авторизацию или управление доступом. Например, декоратор @login_required может проверять, авторизован ли пользователь, перед выполнением функции. Это делает код более безопасным и структурированным, а также упрощает добавление новых функций в будущем.
Используйте декораторы для создания модульного и переиспользуемого кода. Они помогают разделять задачи, упрощают тестирование и делают ваш код более гибким. Начните с простых примеров, таких как замер времени или кэширование, и постепенно внедряйте более сложные решения в свои проекты.
Примеры декораторов в Python: Как улучшить код с их помощью
Используйте декораторы для логирования вызовов функций. Это поможет отслеживать выполнение программы без изменения основной логики. Пример:
def log(func):
def wrapper(*args, **kwargs):
print(f"Вызов функции {func.__name__} с аргументами {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"Функция {func.__name__} завершила выполнение")
return result
return wrapper
@log
def add(a, b):
return a + b
Применяйте декораторы для кэширования результатов вычислений. Это особенно полезно для функций с долгими вычислениями или повторяющимися запросами:
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
Создайте декоратор для проверки прав доступа. Это упростит управление доступом к методам или функциям:
def check_permission(role):
def decorator(func):
def wrapper(*args, **kwargs):
if role == "admin":
return func(*args, **kwargs)
else:
raise PermissionError("Доступ запрещен")
return wrapper
return decorator
@check_permission("admin")
def delete_user(user_id):
# Логика удаления пользователя
pass
Используйте декораторы для измерения времени выполнения функций. Это поможет выявить узкие места в коде:
import time
def timer(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
@timer
def slow_function():
time.sleep(2)
Создавайте декораторы для валидации входных данных. Это сделает код более безопасным и устойчивым к ошибкам:
def validate_input(func):
def wrapper(*args, **kwargs):
for arg in args:
if not isinstance(arg, int):
raise ValueError("Аргументы должны быть целыми числами")
return func(*args, **kwargs)
return wrapper
@validate_input
def multiply(a, b):
return a * b
Декораторы позволяют добавлять функциональность без изменения исходного кода. Используйте их для улучшения читаемости, производительности и безопасности вашего кода.
Основные типы декораторов в Python
Декораторы в Python можно разделить на несколько категорий в зависимости от их функциональности. Используйте их для упрощения кода, добавления логирования, кэширования или контроля доступа.
Функциональные декораторы применяются к функциям. Например, декоратор @staticmethod
позволяет вызывать метод без создания экземпляра класса. Декоратор @property
превращает метод в атрибут, что упрощает доступ к данным.
Классовые декораторы изменяют поведение классов. С их помощью можно добавить методы, изменить атрибуты или автоматизировать процессы. Пример – декоратор @dataclass
, который автоматически генерирует методы __init__
, __repr__
и __eq__
.
Декораторы с параметрами позволяют настраивать их поведение. Например, декоратор @lru_cache(maxsize=128)
кэширует результаты функции, ограничивая размер кэша.
Пользовательские декораторы создаются для решения конкретных задач. Например, можно написать декоратор для измерения времени выполнения функции или проверки прав доступа.
Тип декоратора | Пример | Назначение |
---|---|---|
Функциональный | @staticmethod |
Вызов метода без экземпляра класса |
Классовый | @dataclass |
Автоматизация методов класса |
С параметрами | @lru_cache(maxsize=128) |
Кэширование результатов |
Пользовательский | @measure_time |
Измерение времени выполнения |
Выбирайте тип декоратора в зависимости от задачи. Использование декораторов делает код более читаемым и поддерживаемым.
Что такое декоратор и как он работает?
Работа декоратора начинается с его определения. Например, функция my_decorator
принимает функцию func
как аргумент. Внутри декоратора создаётся новая функция-обёртка, которая выполняет дополнительные действия до или после вызова func
. Возвращая обёртку, декоратор заменяет исходную функцию на расширенную версию.
Применение декоратора выглядит просто: перед определением функции добавьте символ @
и имя декоратора. Например, @my_decorator
перед функцией my_function
автоматически применит логику декоратора к my_function
.
Декораторы могут быть вложенными: один декоратор может применяться поверх другого. Порядок их использования важен, так как он определяет последовательность выполнения. Например, @decorator1
и @decorator2
над функцией приведут к выполнению сначала decorator2
, а затем decorator1
.
Используйте декораторы для повторяющихся задач, чтобы избежать дублирования кода. Например, декоратор @cache
из модуля functools
сохраняет результаты выполнения функции, ускоряя повторные вызовы с теми же аргументами.
Функциональные декораторы: примеры использования
def log_calls(func):
def wrapper(*args, **kwargs):
print(f"Вызов функции {func.__name__} с аргументами {args}, {kwargs}")
result = func(*args, **kwargs)
return result
return wrapper
@log_calls
def add(a, b):
return a + b
Применяйте декораторы для измерения времени выполнения функций. Это полезно для оптимизации кода. Создайте декоратор measure_time
, который будет замерять время выполнения:
import time
def measure_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} секунд")
return result
return wrapper
@measure_time
def slow_function():
time.sleep(2)
Используйте декораторы для кеширования результатов функций. Это особенно полезно для функций с тяжелыми вычислениями. Создайте декоратор cache
, который будет сохранять результаты вызовов:
def cache(func):
cached_results = {}
def wrapper(*args):
if args in cached_results:
return cached_results[args]
result = func(*args)
cached_results[args] = result
return result
return wrapper
@cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # Быстрый результат благодаря кешированию
Декораторы также могут быть полезны для проверки прав доступа. Например, создайте декоратор check_auth
, который будет проверять, авторизован ли пользователь:
def check_auth(func):
def wrapper(*args, **kwargs):
if user_is_authenticated():
return func(*args, **kwargs)
else:
raise PermissionError("Доступ запрещен")
return wrapper
@check_auth
def sensitive_data():
return "Конфиденциальная информация"
print(sensitive_data()) # Проверка доступа перед выполнением
Эти примеры показывают, как декораторы могут упростить и улучшить ваш код, добавляя функциональность без изменения самой функции.
Классовые декораторы: что нужно знать?
Используйте классовые декораторы, когда нужно добавить функциональность к методам или всему классу. В отличие от функций, классовые декораторы позволяют сохранять состояние между вызовами, что делает их удобными для более сложных задач. Например, создайте декоратор класса, который будет логировать вызовы всех его методов:
class LoggerDecorator:
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwargs):
instance = self.cls(*args, **kwargs)
for name, method in self.cls.__dict__.items():
if callable(method):
setattr(instance, name, self._wrap_method(method))
return instance
def _wrap_method(self, method):
def wrapped(*args, **kwargs):
print(f"Вызов метода {method.__name__}")
return method(*args, **kwargs)
return wrapped
@LoggerDecorator
class MyClass:
def method1(self):
print("Метод 1")
def method2(self):
print("Метод 2")
obj = MyClass()
obj.method1()
obj.method2()
Такой подход упрощает добавление логирования без изменения исходного кода класса. Вы можете расширить этот декоратор, добавив проверки, кэширование или другие функции.
Классовые декораторы также полезны для реализации шаблонов проектирования, таких как Singleton. Например, создайте декоратор, который гарантирует, что класс будет иметь только один экземпляр:
class SingletonDecorator:
def __init__(self, cls):
self.cls = cls
self.instance = None
def __call__(self, *args, **kwargs):
if self.instance is None:
self.instance = self.cls(*args, **kwargs)
return self.instance
@SingletonDecorator
class Database:
def __init__(self):
print("Инициализация базы данных")
db1 = Database()
db2 = Database()
print(db1 is db2) # True
Этот пример демонстрирует, как классовые декораторы помогают управлять жизненным циклом объектов. Используйте их для решения задач, где требуется гибкость и контроль над поведением классов.
Параметризованные декораторы: расширение возможностей
Создавайте параметризованные декораторы, чтобы добавлять гибкость в ваш код. Например, декоратор, который принимает аргумент для настройки поведения, позволяет повторно использовать одну функцию для разных задач. Рассмотрим декоратор, который ограничивает количество вызовов функции:
def limit_calls(max_calls):
def decorator(func):
calls = 0
def wrapper(*args, **kwargs):
nonlocal calls
if calls < max_calls:
calls += 1
return func(*args, **kwargs)
else:
raise Exception("Превышено допустимое количество вызовов")
return wrapper
return decorator
@limit_calls(3)
def greet(name):
print(f"Привет, {name}!")
greet("Анна") # Работает
greet("Иван") # Работает
greet("Мария") # Работает
greet("Петр") # Вызывает исключение
Такой подход упрощает управление логикой, не требуя изменений в самой функции. Вы можете легко адаптировать декоратор для других задач, например, для логирования, кэширования или проверки прав доступа.
Используйте вложенные функции для создания параметризованных декораторов. Это позволяет сохранять состояние между вызовами и делает код более модульным. Например, декоратор для измерения времени выполнения функции с возможностью указания единиц измерения:
import time
def timer(unit="seconds"):
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
duration = end - start
if unit == "milliseconds":
duration *= 1000
print(f"Функция {func.__name__} выполнена за {duration:.2f} {unit}")
return result
return wrapper
return decorator
@timer(unit="milliseconds")
def long_running_task():
time.sleep(2)
Параметризованные декораторы делают ваш код более универсальным и упрощают его поддержку. Экспериментируйте с ними, чтобы находить оптимальные решения для ваших задач.
Практическое применение декораторов для улучшения гибкости кода
Декораторы в Python позволяют добавлять функциональность к существующим функциям или методам без изменения их исходного кода. Это особенно полезно, когда нужно внедрить повторяющиеся задачи, такие как логирование, проверка прав доступа или кэширование.
Например, создайте декоратор для логирования времени выполнения функции:
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 example_function():
time.sleep(2)
example_function()
Используйте декораторы для проверки прав доступа:
def check_permission(role):
def decorator(func):
def wrapper(*args, **kwargs):
if role == "admin":
return func(*args, **kwargs)
else:
raise PermissionError("Доступ запрещен")
return wrapper
return decorator
@check_permission("admin")
def sensitive_operation():
print("Операция выполнена")
sensitive_operation()
Этот подход позволяет гибко управлять доступом к функциям в зависимости от роли пользователя.
Декораторы также полезны для кэширования результатов функций, что может значительно ускорить выполнение программ:
def cache_results(func):
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
cache[args] = result
return result
return wrapper
@cache_results
def expensive_computation(x):
return x * x
print(expensive_computation(4))
print(expensive_computation(4)) # Результат будет взят из кэша
Используя декораторы, вы можете легко добавлять новую функциональность, сохраняя код чистым и модульным.
Логирование и трассировка с помощью декораторов
Используйте декораторы для автоматизации логирования вызовов функций. Это упрощает отладку и анализ работы программы. Например, создайте декоратор, который записывает время выполнения функции и её аргументы:
import logging
import time
def log_execution(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
logging.info(f"Функция {func.__name__} выполнена за {end_time - start_time:.2f} секунд с аргументами {args}, {kwargs}")
return result
return wrapper
Примените этот декоратор к любой функции, чтобы отслеживать её поведение. Например:
@log_execution
def calculate_sum(a, b):
return a + b
Теперь каждый вызов calculate_sum
будет записываться в лог, что помогает анализировать производительность и находить узкие места.
Для трассировки вызовов функций добавьте декоратор, который фиксирует вход и выход из функции. Это полезно для понимания последовательности выполнения кода:
def trace_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
Используйте оба декоратора вместе, чтобы получить полную картину выполнения программы:
@log_execution
@trace_calls
def complex_operation(x, y):
return x * y
Такие декораторы не только упрощают отладку, но и делают код чище, избавляя от необходимости вручную добавлять логирование в каждую функцию.
Управление кэшированием: как ускорить выполнение
Используйте декоратор @lru_cache
из модуля functools
, чтобы кэшировать результаты функций. Это особенно полезно для рекурсивных или вычислительно сложных операций. Например, для вычисления чисел Фибоначчи кэширование сократит время выполнения с экспоненциального до линейного.
Укажите параметр maxsize
в @lru_cache
, чтобы ограничить количество хранимых результатов. Это предотвращает избыточное использование памяти. Например, @lru_cache(maxsize=128)
сохраняет последние 128 вызовов функции.
Для функций с изменяемыми аргументами преобразуйте их в неизменяемые типы. Например, используйте кортежи вместо списков, так как кэширование работает только с хэшируемыми объектами.
Если функция зависит от внешних данных, добавьте механизм сброса кэша. Используйте метод cache_clear()
, чтобы очистить кэш при изменении входных данных или условий.
Для кэширования результатов, которые зависят от времени, добавьте тайм-аут. Создайте собственный декоратор, который проверяет время последнего вызова и обновляет кэш, если прошло больше заданного интервала.
Используйте кэширование для функций, которые часто вызываются с одинаковыми аргументами. Это снизит нагрузку на процессор и ускорит выполнение программы.
Аутентификация и авторизация через декораторы
Для реализации аутентификации и авторизации в Python используйте декораторы. Создайте функцию-декоратор, которая проверяет наличие токена или прав доступа у пользователя. Например, декоратор @login_required
может проверять, авторизован ли пользователь, перед выполнением функции.
Вот пример простого декоратора для проверки аутентификации:
def login_required(func):
def wrapper(*args, **kwargs):
if not user_is_authenticated():
raise PermissionError("Требуется авторизация")
return func(*args, **kwargs)
return wrapper
Для авторизации добавьте проверку ролей пользователя. Например, декоратор @admin_required
может ограничивать доступ к функции только для администраторов:
def admin_required(func):
def wrapper(*args, **kwargs):
if not user_is_admin():
raise PermissionError("Доступ запрещен")
return func(*args, **kwargs)
return wrapper
Комбинируйте декораторы для сложных сценариев. Например, сначала проверьте аутентификацию, затем права доступа:
@login_required
@admin_required
def delete_user(user_id):
# Логика удаления пользователя
pass
Используйте библиотеки, такие как Flask-Login или Django, для упрощения работы с аутентификацией. Эти инструменты предоставляют готовые декораторы, которые легко интегрировать в проект.
Тестируйте декораторы на всех этапах разработки. Убедитесь, что они корректно обрабатывают ошибки и не пропускают неавторизованных пользователей. Это поможет избежать уязвимостей в системе безопасности.