Реализация: классы Contact и AddressBook

Реализация: классы Contact и AddressBook

Скелет проекта готов. Пора наполнить классы реальной логикой. В этом уроке реализуем Contact и AddressBook полностью — без заглушек pass.

Класс Contact

Contact — простой класс-контейнер данных. Его задача: хранить поля и уметь представить себя строкой:

# contact.py

class ContactNotFoundError(Exception):
    """Выбрасывается когда контакт не найден в AddressBook."""
    pass

class Contact:
    def __init__(self, name: str, phone: str, email: str = None):
        if not name.strip():
            raise ValueError("Имя не может быть пустым")
        if not phone.strip():
            raise ValueError("Телефон не может быть пустым")
        self.name = name.strip()
        self.phone = phone.strip()
        self.email = email.strip() if email else None

    def __str__(self):
        line = f"{self.name}: {self.phone}"
        if self.email:
            line += f"  ({self.email})"
        return line

    def __repr__(self):
        return f"Contact({self.name!r}, {self.phone!r}, email={self.email!r})"

    def __eq__(self, other):
        if not isinstance(other, Contact):
            return NotImplemented
        return self.name.lower() == other.name.lower()

Ключевые решения:

  • name.strip() — убираем лишние пробелы при создании
  • __eq__ сравнивает без учёта регистра — «Алиса» и «алиса» — один контакт
  • email необязательный (значение по умолчанию None)

Класс AddressBook: поиск и удаление

# address_book.py

from contact import Contact, ContactNotFoundError

class AddressBook:
    def __init__(self, filepath: str):
        self.filepath = filepath
        self._contacts: list = []
        self._load()

    def add(self, contact: Contact) -> None:
        """Добавить контакт. Если имя уже есть — заменить."""
        for i, existing in enumerate(self._contacts):
            if existing.name.lower() == contact.name.lower():
                self._contacts[i] = contact
                return
        self._contacts.append(contact)

    def find(self, name: str) -> Contact:
        """Найти контакт по имени (без учёта регистра)."""
        name_lower = name.lower()
        for contact in self._contacts:
            if contact.name.lower() == name_lower:
                return contact
        raise ContactNotFoundError(f"Контакт '{name}' не найден")

    def delete(self, name: str) -> None:
        """Удалить контакт по имени."""
        contact = self.find(name)   # выбросит ContactNotFoundError если нет
        self._contacts.remove(contact)

    def all(self) -> list:
        """Вернуть все контакты, отсортированные по имени."""
        return sorted(self._contacts, key=lambda c: c.name.lower())

    def search(self, query: str) -> list:
        """Поиск по частичному совпадению имени или телефона."""
        q = query.lower()
        return [
            c for c in self._contacts
            if q in c.name.lower() or q in c.phone
        ]

    def _load(self) -> None:
        pass   # реализуем в следующем уроке

    def save(self) -> None:
        pass   # реализуем в следующем уроке

Тестирование в main.py

Проверим, что классы работают правильно, прежде чем добавлять сохранение:

# main.py (временно — для проверки)
from contact import Contact, ContactNotFoundError
from address_book import AddressBook

book = AddressBook("contacts.json")

book.add(Contact("Алиса", "+7-999-111-22-33", "alice@example.com"))
book.add(Contact("Борис", "+7-999-444-55-66"))
book.add(Contact("Вера",  "+7-999-777-88-99", "vera@example.com"))

for c in book.all():
    print(c)

print()
try:
    found = book.find("Борис")
    print(f"Найден: {found}")
    book.delete("Борис")
    print(f"После удаления: {len(book.all())} контактов")
except ContactNotFoundError as e:
    print(f"Ошибка: {e}")

Вывод:

Алиса: +7-999-111-22-33  (alice@example.com)
Борис: +7-999-444-55-66
Вера: +7-999-777-88-99  (vera@example.com)

Найден: Борис: +7-999-444-55-66
После удаления: 2 контактов

Итог

  • Contact хранит имя, телефон, email; валидирует в __init__; умеет красиво выводиться.
  • AddressBook хранит список контактов; поддерживает add, find, delete, all, search.
  • find() выбрасывает ContactNotFoundError вместо возврата None — вызывающий код явно обрабатывает ошибку.
  • add() заменяет контакт при совпадении имени — нет дубликатов.

В следующем уроке реализуем _load() и save() — сохранение данных в JSON между сессиями.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике