Практика: документируем классы из предыдущих уроков
Теория позади. Теперь откроем два класса, которые вы уже знаете — BankAccount и Vector — и добавим к ним полноценную документацию: docstring в Google Style и type hints. Потом проверим результат через help(), pydoc и mypy.
🎯 Что мы будем делать
В прошлых модулях мы написали два класса. Они работают правильно, но у них нет документации — ни docstring, ни type hints. Любой, кто откроет этот код, будет вынужден читать каждую строку, чтобы понять, что делают методы.
help() давал полезный вывод, а mypy находил ошибки ещё до запуска.
🏦 Шаг 1 — BankAccount: исходный код
Вот как выглядит класс до документирования. Код работает, но ничего не объясняет:
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("Сумма должна быть положительной")
self.balance += amount
return self.balance
def withdraw(self, amount):
if amount <= 0:
raise ValueError("Сумма должна быть положительной")
if amount > self.balance:
raise ValueError("Недостаточно средств")
self.balance -= amount
return self.balance
def get_info(self):
return f"{self.owner}: {self.balance} руб."
Посмотрите: что делает deposit? Возвращает что-то — но что именно? Какой тип у balance? Без чтения тела не скажешь. Именно это мы и исправим.
✏️ Шаг 2 — BankAccount: добавляем документацию
Добавляем Google Style docstring и type hints. Создайте файл bank_account.py с таким содержимым:
class BankAccount:
"""Банковский счёт клиента.
Хранит текущий баланс и позволяет выполнять базовые
финансовые операции: пополнение и снятие средств.
Attributes:
owner (str): Имя владельца счёта.
balance (float): Текущий баланс в рублях.
Example:
>>> account = BankAccount("Алиса", 1000.0)
>>> account.deposit(500.0)
1500.0
>>> account.withdraw(200.0)
1300.0
>>> print(account.get_info())
Алиса: 1300.0 руб.
"""
def __init__(self, owner: str, balance: float = 0.0) -> None:
"""Инициализирует банковский счёт.
Args:
owner: Имя владельца счёта.
balance: Начальный баланс в рублях. По умолчанию 0.0.
"""
self.owner = owner
self.balance = balance
def deposit(self, amount: float) -> float:
"""Пополняет счёт на указанную сумму.
Args:
amount: Сумма пополнения в рублях. Должна быть положительной.
Returns:
Новый баланс после пополнения.
Raises:
ValueError: Если сумма меньше или равна нулю.
Example:
>>> account = BankAccount("Боб", 100.0)
>>> account.deposit(50.0)
150.0
"""
if amount <= 0:
raise ValueError("Сумма должна быть положительной")
self.balance += amount
return self.balance
def withdraw(self, amount: float) -> float:
"""Снимает деньги со счёта.
Args:
amount: Сумма снятия в рублях. Должна быть положительной
и не превышать текущий баланс.
Returns:
Новый баланс после снятия.
Raises:
ValueError: Если сумма меньше или равна нулю,
или если на счёте недостаточно средств.
Example:
>>> account = BankAccount("Боб", 200.0)
>>> account.withdraw(50.0)
150.0
"""
if amount <= 0:
raise ValueError("Сумма должна быть положительной")
if amount > self.balance:
raise ValueError("Недостаточно средств")
self.balance -= amount
return self.balance
def get_info(self) -> str:
"""Возвращает строку с информацией о счёте.
Returns:
Строка вида «Имя: баланс руб.»
Example:
>>> account = BankAccount("Алиса", 500.0)
>>> account.get_info()
'Алиса: 500.0 руб.'
"""
return f"{self.owner}: {self.balance} руб."
str, float), каждый метод описывает что возвращает (Returns) и что может бросить (Raises). Класс получил пример использования прямо в docstring.
📐 Шаг 3 — Vector: исходный код
Теперь второй класс. Исходный вариант без документации:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def length(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
def __repr__(self):
return f"Vector({self.x}, {self.y})"
Что принимает __mul__ — вектор или число? Что возвращает length — и в каких единицах? Без документации непонятно.
✏️ Шаг 4 — Vector: добавляем документацию
Создайте файл vector.py:
class Vector:
"""Двумерный математический вектор.
Представляет вектор на плоскости с координатами x и y.
Поддерживает сложение двух векторов и умножение на скаляр.
Attributes:
x (float): Координата по горизонтальной оси.
y (float): Координата по вертикальной оси.
Example:
>>> v1 = Vector(1.0, 2.0)
>>> v2 = Vector(3.0, 4.0)
>>> v1 + v2
Vector(4.0, 6.0)
>>> v1 * 3
Vector(3.0, 6.0)
>>> v1.length()
2.23606797749979
"""
def __init__(self, x: float, y: float) -> None:
"""Инициализирует вектор с заданными координатами.
Args:
x: Координата по горизонтальной оси.
y: Координата по вертикальной оси.
"""
self.x = x
self.y = y
def __add__(self, other: "Vector") -> "Vector":
"""Складывает два вектора покоординатно.
Args:
other: Второй вектор для сложения.
Returns:
Новый вектор, являющийся суммой двух векторов.
Example:
>>> Vector(1.0, 2.0) + Vector(3.0, 4.0)
Vector(4.0, 6.0)
"""
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar: float) -> "Vector":
"""Умножает вектор на скалярное число.
Args:
scalar: Число, на которое умножается вектор.
Returns:
Новый вектор, координаты которого умножены на scalar.
Example:
>>> Vector(2.0, 3.0) * 2
Vector(4.0, 6.0)
"""
return Vector(self.x * scalar, self.y * scalar)
def length(self) -> float:
"""Вычисляет длину (модуль) вектора.
Длина рассчитывается по теореме Пифагора: sqrt(x² + y²).
Returns:
Длина вектора в виде числа с плавающей точкой.
Example:
>>> Vector(3.0, 4.0).length()
5.0
"""
return (self.x ** 2 + self.y ** 2) ** 0.5
def __repr__(self) -> str:
"""Возвращает строковое представление вектора.
Returns:
Строка вида «Vector(x, y)», пригодная для отладки.
Example:
>>> repr(Vector(1.0, 2.0))
'Vector(1.0, 2.0)'
"""
return f"Vector({self.x}, {self.y})"
"Vector" написан в кавычках — это называется forward reference. Когда Python читает аннотацию __add__, класс Vector ещё не до конца определён, поэтому используем строку. Начиная с Python 3.10 это можно делать без кавычек через from __future__ import annotations.
🔍 Шаг 5 — Запускаем help()
Теперь проверим результат. Откройте терминал, запустите Python и введите:
from bank_account import BankAccount
help(BankAccount)
Python выведет примерно такой результат:
Help on class BankAccount in module bank_account:
class BankAccount(builtins.object)
| BankAccount(owner: str, balance: float = 0.0) -> None
|
| Банковский счёт клиента.
|
| Хранит текущий баланс и позволяет выполнять базовые
| финансовые операции: пополнение и снятие средств.
|
| Attributes:
| owner (str): Имя владельца счёта.
| balance (float): Текущий баланс в рублях.
|
| Example:
| >>> account = BankAccount("Алиса", 1000.0)
| >>> account.deposit(500.0)
| 1500.0
|
| Methods defined here:
|
| deposit(self, amount: float) -> float
| Пополняет счёт на указанную сумму.
|
| Args:
| amount: Сумма пополнения в рублях. Должна быть положительной.
|
| Returns:
| Новый баланс после пополнения.
|
| Raises:
| ValueError: Если сумма меньше или равна нулю.
| ...
Отлично! Python сам собрал все docstring и показал их в удобном виде. Именно это вы увидите в IDE, когда наведёте курсор на метод.
📄 Шаг 6 — Генерируем документацию через pydoc
pydoc — это встроенный инструмент Python, который генерирует документацию прямо из исходного кода. Перейдите в папку с файлом и выполните:
# Текстовый вывод в терминал
python -m pydoc bank_account
# HTML-файл (откроется в браузере)
python -m pydoc -w bank_account
После второй команды появится файл bank_account.html. Откройте его в браузере — увидите страницу документации с заголовком, всеми методами и их описаниями. Именно так это выглядит в профессиональных проектах.
🔬 Шаг 7 — Проверяем типы через mypy
mypy — статический анализатор типов. Он читает ваши type hints и проверяет, нет ли где несоответствий — без запуска кода. Установите и запустите:
# Установка (один раз)
pip install mypy
# Проверка файла
mypy bank_account.py
Если типы расставлены правильно, mypy выведет:
Success: no issues found in 1 source file
Теперь намеренно сломаем тип — добавим в bank_account.py такой вызов:
# bank_account.py (в конце файла добавьте)
account = BankAccount("Тест")
account.deposit("сто рублей") # строка вместо float!
Снова запустите mypy bank_account.py. Теперь вы увидите:
bank_account.py:XX: error: Argument 1 to "deposit" has incompatible type "str"; expected "float"
Found 1 error in 1 file (checked 1 source file)
Уберите сломанный код после проверки — пусть файл остаётся чистым.
🏁 Итоги урока
В этом уроке мы взяли два реальных класса и превратили их в профессионально задокументированный код:
- Написали Google Style docstring для классов и каждого метода
- Добавили type hints ко всем параметрам и возвращаемым значениям
- Использовали forward reference (
"Vector") для аннотации внутри класса - Проверили результат через help() — документация отображается корректно
- Сгенерировали HTML-документацию через pydoc -w
- Убедились, что mypy находит ошибки типов до запуска программы
Теперь у вас есть полный рабочий процесс: написал класс → задокументировал → проверил типы. Этот подход используют во всех серьёзных Python-проектах.