⚖️ Старый и современный подход

⚡ Различия в двух словах

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

Новый: аннотации типов, pathlib, functools.partial, явный стиль и избегание изменяемых значений по умолчанию.

Вывод: современный код проще читать и безопаснее поддерживать.

📜 Старый подход: без аннотаций типов

def find_numbers(text):
    import re
    return re.findall(r"\d+", text)


print(find_numbers("Version 3.10, Build 2023"))

Что здесь происходит: код работает, но непонятно, какие типы ожидает функция и что она возвращает.

✅ Современный вариант

import re


def find_numbers(text: str) -> list[str]:
    return re.findall(r"\d+", text)


print(find_numbers("Version 3.10, Build 2023"))

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

Преимущества:
  • Меньше неожиданных ошибок при передаче аргументов.
  • Лучше работают type hints, mypy и IDE.
  • Код самодокументируемый.

📜 Старый подход: строковые пути

import os


path = "data/reports/report.txt"
if os.path.exists(path):
    with open(path, "r", encoding="utf-8") as file:
        content = file.read()

Что здесь происходит: путь представлен строкой, операции соединения зависят от операционной системы.

✅ Современный вариант: pathlib

from pathlib import Path


path = Path("data") / "reports" / "report.txt"
if path.exists():
    content = path.read_text(encoding="utf-8")

Что улучшилось: pathlib делает пути объектами, автоматически решает проблему разделителей и предоставляет удобные методы чтения и записи.

Проверить по документации: подробнее о возможностях pathlib — в документации pathlib.

📜 Ручная фабрика для простых случаев

def make_adder(value):
    def adder(x):
        return x + value
    return adder


add_five = make_adder(5)
print(add_five(3))

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

✅ Современный вариант: functools.partial

from functools import partial


def add(a: float, b: float) -> float:
    return a + b


add_five = partial(add, 5)
print(add_five(3))

Что улучшилось: встроенная функция functools.partial делает намерение очевидным и не требует вложенных функций для простых сценариев.

Проверить по документации: functools.partial создаёт вызываемый объект, а не обычную функцию. Подробнее — в документации functools.

📜 Изменяемые значения по умолчанию

def append_event(event, events=[]):
    events.append(event)
    return events


print(append_event("login"))
print(append_event("logout"))

Что здесь происходит: список создаётся один раз при определении функции и разделяется между всеми вызовами. Это частая ловушка даже внутри замыканий.

✅ Современный вариант: None вместо изменяемого значения

def append_event(event: str, events: list[str] | None = None) -> list[str]:
    if events is None:
        events = []
    events.append(event)
    return events


print(append_event("login"))
print(append_event("logout"))

Что улучшилось: каждый вызов получает свой независимый список. Это соответствует рекомендации PEP 8.

✅ Современный стиль: PEP 8

PEP 8 — руководство по стилю кода Python. Основные рекомендации:

  • Имена функций и переменных — snake_case.
  • Имена констант — UPPER_SNAKE_CASE.
  • Отступы — 4 пробела.
  • Длина строки — не более 79–88 символов.
  • Импорты группируются: стандартная библиотека, сторонние модули, локальные модули.
Проверить по документации: полный текст руководства — в PEP 8.

🕰️ Когда старый подход ещё можно встретить

Учебные материалы и legacy-код могут не использовать аннотации типов, pathlib или functools.partial. Это нормально, но в новых проектах рекомендуется использовать современный стиль. Аннотации str | None требуют Python 3.10+.