Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое декораторы в Python?
Декоратор — это функция, которая принимает другую функцию как аргумент, добавляет к ней дополнительную функциональность и возвращает модифицированную версию. Декораторы — это форма метапрограммирования, позволяющая изменять поведение функций и классов без изменения их исходного кода.
Декораторы основаны на концепции higher-order functions (функции, которые работают с функциями как с объектами данных).
Базовая концепция
В Python функции — это объекты первого класса, их можно:
- Передавать как аргументы
- Возвращать из других функций
- Присваивать переменным
- Хранить в структурах данных
# Функция как объект
def greet():
return "Hello"
# Присвоить переменной
greeting = greet
print(greeting()) # Output: Hello
# Передать как аргумент
def execute(func):
return func()
print(execute(greet)) # Output: Hello
Как работают декораторы
# Простой декоратор
def my_decorator(func):
def wrapper(*args, **kwargs):
print(f"Функция {func.__name__} начала выполнение")
result = func(*args, **kwargs)
print(f"Функция {func.__name__} завершила выполнение")
return result
return wrapper
# Применение декоратора
@my_decorator
def say_hello(name):
return f"Hello, {name}!"
# Эквивалентно:
# say_hello = my_decorator(say_hello)
print(say_hello("Alice"))
Вывод:
Функция say_hello начала выполнение
Hello, Alice!
Функция say_hello завершила выполнение
Практические примеры для Data Engineer
1. Логирование времени выполнения (profiling)
import time
from functools import wraps
def timing_decorator(func):
@wraps(func) # сохраняет оригинальные метаданные функции
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - start_time
print(f"{func.__name__} took {elapsed:.2f} seconds")
return result
return wrapper
@timing_decorator
def process_large_dataset(df):
# долгая операция
return df.groupby(category).sum()
process_large_dataset(data) # Output: process_large_dataset took 2.34 seconds
2. Retry механизм для API запросов
import time
from functools import wraps
def retry(max_attempts=3, delay=2):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"Attempt {attempt + 1} failed, retrying in {delay}s...")
time.sleep(delay)
return wrapper
return decorator
@retry(max_attempts=3, delay=2)
def fetch_data_from_api(url):
# может выбросить исключение
response = requests.get(url)
return response.json()
3. Кэширование результатов (memoization)
from functools import lru_cache
@lru_cache(maxsize=128)
def expensive_computation(n):
# долгая операция
return sum(range(n))
print(expensive_computation(1000)) # вычисляет
print(expensive_computation(1000)) # возвращает из кэша
4. Валидация аргументов
from functools import wraps
def validate_args(func):
@wraps(func)
def wrapper(*args, **kwargs):
# проверяем, что все аргументы положительные
for arg in args:
if isinstance(arg, (int, float)) and arg < 0:
raise ValueError(f"Negative argument: {arg}")
return func(*args, **kwargs)
return wrapper
@validate_args
def calculate_percentage(total, part):
return (part / total) * 100
print(calculate_percentage(100, 25)) # Output: 25.0
print(calculate_percentage(100, -25)) # Raises ValueError
5. Декоратор для преобразования результата
from functools import wraps
import json
def json_response(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return json.dumps(result, indent=2)
return wrapper
@json_response
def get_user_info():
return {"id": 1, "name": "Alice", "email": "alice@example.com"}
print(get_user_info())
Декораторы с параметрами
Декоратор может сам принимать параметры (требуется дополнительный уровень функций):
from functools import wraps
def repeat(times):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for i in range(times):
print(f"Execution {i + 1}")
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Bob")
Вывод:
Execution 1
Hello, Bob!
Execution 2
Hello, Bob!
Execution 3
Hello, Bob!
Декораторы классов
Декораторы работают и с классами:
from functools import wraps
def add_repr(cls):
original_repr = cls.__repr__
def new_repr(self):
return f"{cls.__name__}({self.__dict__})"
cls.__repr__ = new_repr
return cls
@add_repr
class User:
def __init__(self, name, age):
self.name = name
self.age = age
user = User("Alice", 30)
print(user) # Output: User({name: Alice, age: 30})
Встроенные декораторы Python
- @property — превращает метод в атрибут
- @staticmethod — статический метод класса
- @classmethod — метод класса
- @functools.wraps — сохраняет метаданные функции
- @functools.lru_cache — кэширование результатов
- @functools.singledispatch — полиморфизм на основе типа
Ключевые преимущества
- Переиспользование кода — декоратор применяется к многим функциям
- Разделение ответственности — логика добавляется отдельно от основного кода
- Улучшение читаемости — синтаксис @ делает явным, что функция модифицирована
- Гибкость — можно комбинировать несколько декораторов
Множественные декораторы
@timing_decorator
@retry(max_attempts=3)
@validate_args
def process_data(filename):
# код...
pass
# Применяются в обратном порядке (от низа к верхe)
# 1. validate_args
# 2. retry
# 3. timing_decorator