📜 Старый подход: без аннотаций типов
def multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = multiplier(2)
print(double(5))
Что здесь происходит: код работает, но непонятно, какие типы ожидают параметры и что возвращает функция. Приходится читать тело функции.
✅ Современный вариант
def multiplier(factor: float):
def multiply(number: float) -> float:
return number * factor
return multiply
double = multiplier(2)
print(double(5)) # 10.0
Что улучшилось: аннотации типов делают контракт функции очевидным. Редакторы подсказывают ошибки, а читатели сразу видят, какие аргументы передавать.
- Меньше неожиданных ошибок при передаче аргументов.
- Лучше работают type hints, mypy и IDE.
- Код самодокументируемый.
📜 Ручная фабрика для простых случаев
def make_adder(value):
def adder(x):
return x + value
return adder
add_five = make_adder(5)
print(add_five(3)) # 8
Что здесь происходит: замыкание решает задачу, но для простой фиксации аргумента писать две вложенные функции избыточно.
✅ Современный вариант: 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)) # 8
Что улучшилось: встроенная функция functools.partial делает намерение очевидным и не требует вложенных функций для простых сценариев.
functools.partial создаёт вызываемый объект, а не обычную функцию. Подробнее — в документации.
📜 Изменяемые значения по умолчанию
def append_event(event, events=[]):
events.append(event)
return events
print(append_event("login")) # ['login']
print(append_event("logout")) # ['login', '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")) # ['login']
print(append_event("logout")) # ['logout']
Что улучшилось: каждый вызов получает свой независимый список. Это соответствует рекомендации PEP 8.
🕰️ Когда старый подход ещё можно встретить
Учебные материалы и legacy-код могут не использовать аннотации типов или functools.partial. Это нормально, но в новых проектах рекомендуется использовать современный стиль. Аннотации str | None требуют Python 3.10+.