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

Какие знаешь рекомендации при написании функции?

1.7 Middle🔥 201 комментариев
#Python Core

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

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

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

Рекомендации при написании функций

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

1. Одна ответственность (Single Responsibility Principle)

Функция должна делать одно и делать это хорошо:

# Плохо - функция делает слишком много
def process_user_order(user_id, items):
    user = get_user_from_db(user_id)
    for item in items:
        if item["price"] < 0:
            return {"error": "Invalid price"}
    order = create_order(user, items)
    save_order(order)
    send_email(user.email, "Order confirmed")
    log(f"Order {order.id} created")
    return order

# Хорошо - каждая функция имеет одну ответственность
def create_order(user_id: int, items: list) -> Order:
    """Создание заказа после валидации"""
    validate_items(items)
    user = get_user_from_db(user_id)
    return Order.create(user, items)

def validate_items(items: list) -> None:
    """Валидация товаров"""
    for item in items:
        if item["price"] < 0:
            raise ValueError("Invalid price")

2. Чистое имя функции

Имя должно ясно описывать, что делает функция:

# Непонятные имена
def fn(x):
    return x * 2

def do_stuff(data):
    pass

# Описательные имена
def double_integer(value: int) -> int:
    """Удваивает переданное целое число"""
    return value * 2

def calculate_total_price(items: list) -> float:
    """Вычисляет итоговую стоимость товаров с налогом"""
    return sum(item.price for item in items) * 1.2

3. Ограничение параметров (максимум 3-4)

Если параметров больше, используй объект:

# Слишком много параметров
def create_report(user_id, start_date, end_date, format, language, 
                  include_charts, include_summary, send_email, email_address):
    pass

# Группируем в объект
from dataclasses import dataclass

@dataclass
class ReportConfig:
    format: str
    language: str
    include_charts: bool = True
    include_summary: bool = True
    send_email: bool = False
    email_address: str = ""

def create_report(user_id: int, start_date, end_date, 
                  config: ReportConfig) -> Report:
    """Создание отчёта с указанными параметрами"""
    pass

4. Явное лучше неявного

# Неявное поведение
def get_user(user_id):
    try:
        return User.objects.get(id=user_id)
    except:
        return None

# Явное поведение с типами
from typing import Optional

def get_user_by_id(user_id: int) -> Optional[User]:
    """Получает пользователя по ID, возвращает None если не найден"""
    try:
        return User.objects.get(id=user_id)
    except User.DoesNotExist:
        return None

5. Чистые функции (Pure Functions)

Идеально, когда функция не имеет побочных эффектов:

# Нечистая функция - изменяет внешнее состояние
discount = 0.1

def apply_discount(price):
    global discount
    discount = 0.2
    return price * (1 - discount)

# Чистая функция - нет побочных эффектов
def apply_discount(price: float, discount_rate: float) -> float:
    """Применяет скидку к цене"""
    return price * (1 - discount_rate)

6. Обработка ошибок

# Плохо - нет обработки ошибок
def parse_json(data: str) -> dict:
    return json.loads(data)

# Хорошо - явная обработка
from typing import Optional

def parse_json(data: str) -> Optional[dict]:
    """Пытается спарсить JSON, возвращает None при ошибке"""
    try:
        return json.loads(data)
    except json.JSONDecodeError as e:
        logger.error(f"Failed to parse JSON: {e}")
        return None

7. Документация и Type Hints

# Без документации
def calc(a, b):
    return a + b

# С документацией
def calculate_total_amount(base_price: float, tax_rate: float) -> float:
    """Вычисляет итоговую сумму с налогом.
    
    Args:
        base_price: Базовая цена товара
        tax_rate: Налоговая ставка (0.0 - 1.0)
    
    Returns:
        Итоговая цена с налогом
    
    Raises:
        ValueError: Если цена или налог отрицательные
    
    Example:
        calculate_total_amount(100, 0.1) -> 110.0
    """
    if base_price < 0 or tax_rate < 0:
        raise ValueError("Price and tax rate must be positive")
    return base_price * (1 + tax_rate)

8. Избегай магических чисел

# Магические числа
def validate_password(password: str) -> bool:
    return len(password) >= 8

# Именованные константы
MIN_PASSWORD_LENGTH = 8
MAX_PASSWORD_LENGTH = 128

def validate_password(password: str) -> bool:
    """Валидирует пароль по стандартам безопасности"""
    if len(password) < MIN_PASSWORD_LENGTH:
        raise ValueError(f"Password must be at least {MIN_PASSWORD_LENGTH} characters")
    if len(password) > MAX_PASSWORD_LENGTH:
        raise ValueError(f"Password must not exceed {MAX_PASSWORD_LENGTH} characters")
    return True

9. Избегай глубокой вложенности

# Глубокая вложенность - адский код
def process_order(order):
    if order:
        if order.items:
            if order.user:
                if order.user.is_active:
                    if order.total > 0:
                        save_order(order)

# Early return
def process_order(order: Order) -> None:
    """Обрабатывает заказ с валидацией"""
    if not order:
        raise ValueError("Order cannot be None")
    if not order.items:
        raise ValueError("Order must have items")
    if not order.user:
        raise ValueError("Order must have user")
    if not order.user.is_active:
        raise ValueError("User account is not active")
    if order.total <= 0:
        raise ValueError("Order total must be positive")
    
    save_order(order)

10. Тестируемость

# Сложно тестировать - зависимости захардкодены
def send_order_confirmation(order_id: int):
    order = db.query(Order).get(order_id)
    user = db.query(User).get(order.user_id)
    email_service.send(user.email, "Order confirmed")

# Легко тестировать - зависимости инъектируются
from typing import Protocol

class EmailService(Protocol):
    def send(self, email: str, subject: str, body: str) -> None: ...

def send_order_confirmation(order: Order, user: User, 
                           email_service: EmailService) -> None:
    """Отправляет подтверждение заказа"""
    email_service.send(
        user.email,
        "Order Confirmation",
        f"Your order #{order.id} has been confirmed"
    )

Итоговый чеклист

Функция должна:

  • Иметь одну ответственность
  • Иметь понятное имя (что делает, не как)
  • Иметь 3-4 параметра максимум
  • Иметь полную типизацию
  • Иметь документацию (docstring)
  • Избегать побочных эффектов где возможно
  • Явно обрабатывать ошибки
  • Быть легко тестируемой
  • Избегать магических чисел
  • Использовать early return для уменьшения вложенности

Применение этих рекомендаций делает код чище, безопаснее и проще в поддержке.

Какие знаешь рекомендации при написании функции? | PrepBro