📜 Старый подход: без аннотаций типов
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 символов.
- Импорты группируются: стандартная библиотека, сторонние модули, локальные модули.
🕰️ Когда старый подход ещё можно встретить
Учебные материалы и legacy-код могут не использовать аннотации типов, pathlib или functools.partial. Это нормально, но в новых проектах рекомендуется использовать современный стиль. Аннотации str | None требуют Python 3.10+.