Аннотации типов
Python остаётся динамическим языком, но поддерживает необязательные подсказки типов. Интерпретатор их не проверяет — они нужны редакторам, статическим анализаторам и читателям кода.
def add(a: int, b: int) -> int: return a + bname: str = "Anna"scores: list[int] = [5, 4, 5]user: dict[str, int] = {"age": 25}point: tuple[int, int] = (1, 2)Необязательные значения и объединения
def find(items: list[str], key: str) -> str | None: for item in items: if item == key: return item return Nonedef parse(value: int | str) -> int: return int(value)Модуль typing
from typing import Callable, Iterable, Iterator, Any, TypeVar, ProtocolT = TypeVar("T")def first(items: Iterable[T]) -> T: for item in items: return item raise ValueError("пустая последовательность")def apply(func: Callable[[int], int], value: int) -> int: return func(value)class Drawable(Protocol): def draw(self) -> None: ...Протоколы описывают структурную типизацию: объект подходит, если у него есть нужные методы, наследование не требуется.
Статическая проверка
Для проверки используется отдельный инструмент, например mypy:
pip install mypymypy src/Тестирование
unittest
Модуль входит в стандартную библиотеку:
import unittestdef divide(a, b): if b == 0: raise ZeroDivisionError("деление на ноль") return a / bclass DivideTests(unittest.TestCase): def test_normal(self): self.assertEqual(divide(6, 3), 2) def test_zero(self): with self.assertRaises(ZeroDivisionError): divide(1, 0)if __name__ == "__main__": unittest.main()Запуск всех тестов проекта:
python -m unittest discoverpytest
pytest — популярный сторонний фреймворк с более лаконичным синтаксисом:
pip install pytestimport pytestdef divide(a, b): if b == 0: raise ZeroDivisionError("деление на ноль") return a / bdef test_normal(): assert divide(6, 3) == 2def test_zero(): with pytest.raises(ZeroDivisionError): divide(1, 0)@pytest.mark.parametrize("a, b, expected", [(6, 3, 2), (10, 5, 2), (9, 3, 3)])def test_many(a, b, expected): assert divide(a, b) == expectedЗапуск:
pytestФикстуры
import pytest@pytest.fixturedef numbers(): return [1, 2, 3]def test_sum(numbers): assert sum(numbers) == 6Принципы хороших тестов
- Один тест проверяет одно поведение
- Тесты независимы друг от друга и не зависят от порядка запуска
- Тесты быстрые и воспроизводимые
- Название теста описывает проверяемый сценарий
- Проверяйте не только штатные случаи, но и граничные значения и ошибки
Задания
- Добавьте аннотации типов к функциям из предыдущих уроков и прогоните
mypy. - Напишите набор тестов для функции проверки палиндрома, включая пустую строку и регистр букв.
