← Назад к вопросам

Что записывается через @ в Python?

2.3 Middle🔥 51 комментариев
#DevOps и инфраструктура

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

@ — это синтаксический сахар для декораторов (decorators). Декоратор — это функция, которая модифицирует поведение другой функции или класса, не изменяя её исходный код. Это мощный инструмент для добавления функциональности.

Базовая концепция

# ❌ Без декоратора — объемное и повторяющееся
def greet():
    print("Starting greet...")
    print("Hello, World!")
    print("Finished greet.")

# ✅ С декоратором — чистый код
@log_execution
def greet():
    print("Hello, World!")

# Оба варианта работают одинаково, но второй чище

Как работает @

# Это:
@decorator
def func():
    pass

# Эквивалентно:
func = decorator(func)

# Декоратор принимает функцию и возвращает обёрнутую функцию

Простой пример: логирование

def log_execution(func):
    """Декоратор для логирования вызова функции"""
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)  # Вызываем оригинальную функцию
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper

@log_execution
def add(a, b):
    return a + b

# Использование
result = add(2, 3)
# Вывод:
# Calling add with args=(2, 3), kwargs={}
# add returned 5
# result = 5

Декоратор с параметрами

def repeat(times):
    """Декоратор с параметром: повторяет функцию N раз"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            results = []
            for i in range(times):
                result = func(*args, **kwargs)
                results.append(result)
            return results
        return wrapper
    return decorator

@repeat(times=3)
def greet():
    return "Hello!"

result = greet()
print(result)  # ['Hello!', 'Hello!', 'Hello!']

Практический пример: авторизация в веб-приложении

from functools import wraps
from flask import Flask, request

app = Flask(__name__)

def require_auth(func):
    """Декоратор для проверки авторизации"""
    @wraps(func)  # Сохраняет metadata оригинальной функции
    def wrapper(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token:
            return {"error": "No token"}, 401
        
        # Проверяем токен
        user = verify_token(token)
        if not user:
            return {"error": "Invalid token"}, 401
        
        # Передаём user в функцию
        return func(user=user, *args, **kwargs)
    return wrapper

@app.route('/api/profile')
@require_auth
def get_profile(user):
    return {"user_id": user.id, "name": user.name}

def verify_token(token):
    # Логика проверки токена
    if token == "valid-token":
        return {"id": 1, "name": "John"}
    return None

Множественные декораторы

# Декораторы применяются снизу вверх (bottom-to-top)
@log_execution
@check_permission
@require_auth
def delete_user(user_id):
    # ...
    pass

# Эквивалентно:
delete_user = log_execution(
    check_permission(
        require_auth(delete_user)
    )
)

# Порядок выполнения:
# 1. require_auth проверяет авторизацию
# 2. check_permission проверяет права
# 3. log_execution логирует

Декоратор класса

def singleton(cls):
    """Декоратор для паттерна Singleton"""
    instances = {}
    
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    
    return get_instance

@singleton
class DatabaseConnection:
    def __init__(self, url):
        self.url = url

# Всегда вернёт один инстанс
db1 = DatabaseConnection("postgresql://localhost")
db2 = DatabaseConnection("postgresql://localhost")
assert db1 is db2  # True — один объект!

Встроенные декораторы Python

# @property — превращает метод в свойство
class User:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    
    @property
    def full_name(self):
        return f"{self.first_name} {self.last_name}"

user = User("John", "Doe")
print(user.full_name)  # John Doe (выглядит как свойство)

# @staticmethod — не получает self
class Math:
    @staticmethod
    def add(a, b):
        return a + b

Math.add(2, 3)  # 5

# @classmethod — получает cls вместо self
class MyClass:
    count = 0
    
    @classmethod
    def increment(cls):
        cls.count += 1

MyClass.increment()  # Работает с классом, не инстансом

# @abstractmethod — абстрактный метод
from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

Пример из реальной жизни: кеширование

from functools import lru_cache
import time

@lru_cache(maxsize=128)
def expensive_calculation(n):
    """Встроенный декоратор для кеширования результатов"""
    time.sleep(1)  # Имитируем долгую операцию
    return n * n

start = time.time()
print(expensive_calculation(5))  # Медленно, ~1 сек
print(time.time() - start)  # 1.0

start = time.time()
print(expensive_calculation(5))  # Быстро, из кеша
print(time.time() - start)  # 0.0001

Декоратор для проверки типов

def type_check(**type_hints):
    """Проверяет типы аргументов функции"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for key, expected_type in type_hints.items():
                if key in kwargs:
                    if not isinstance(kwargs[key], expected_type):
                        raise TypeError(
                            f"Argument {key} must be {expected_type}, "
                            f"got {type(kwargs[key])}"
                        )
            return func(*args, **kwargs)
        return wrapper
    return decorator

@type_check(name=str, age=int)
def create_user(name, age):
    return {"name": name, "age": age}

create_user(name="John", age=25)  # OK
create_user(name="John", age="25")  # TypeError!

Важные утилиты

from functools import wraps

# ВСЕГДА используй @wraps при создании декораторов
def my_decorator(func):
    @wraps(func)  # ← Важно!
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

# Почему важно:
@my_decorator
def my_function():
    """Это docstring"""
    pass

print(my_function.__name__)  # 'my_function' (не 'wrapper')
print(my_function.__doc__)   # 'Это docstring' (сохранён)

Заключение

@ в Python — это синтаксис для декораторов, которые оборачивают функции и классы, добавляя функциональность. Декораторы используются везде: в фреймворках (Flask, Django), для кеширования, логирования, проверки прав доступа и многого другого. Это один из самых мощных инструментов Python.

Что записывается через @ в Python? | PrepBro