Загрузка данных


# --- Кастомные исключения ---

class ContactNotFoundError(Exception):
    """Контакт с таким именем не найден"""
    def __init__(self, name):
        self.name = name
        super().__init__(f"Контакт \"{name}\" не найден")


class InvalidPhoneError(Exception):
    """Номер телефона содержит недопустимые символы"""
    def __init__(self, phone):
        self.phone = phone
        super().__init__(f"Некорректный номер телефона: \"{phone}\"")


# --- Класс PhoneBook ---

class PhoneBook:
    def __init__(self):
        # Атрибут contacts — словарь вида {имя: номер}
        self.contacts = {}

    def add(self, name: str, phone: str):
        """
        Добавляет контакт. 
        Поднимает InvalidPhoneError, если номер некорректный.
        """
        # Разрешенные символы: цифры, '+', '-', ' ' (пробел)
        # Проверяем каждый символ в строке номера телефона
        allowed_chars = set("0123456789+- ")
        if not all(char in allowed_chars for char in phone):
            raise InvalidPhoneError(phone)
            
        self.contacts[name] = phone

    def find(self, name: str) -> str:
        """
        Возвращает номер по имени.
        Поднимает ContactNotFoundError, если контакта нет.
        """
        if name not in self.contacts:
            raise ContactNotFoundError(name)
        return self.contacts[name]

    def delete(self, name: str):
        """
        Удаляет контакт.
        Поднимает ContactNotFoundError, если контакта нет.
        """
        if name not in self.contacts:
            raise ContactNotFoundError(name)
        del self.contacts[name]

    def find_safe(self, name: str):
        """
        Как find, но возвращает None вместо исключения.
        """
        try:
            return self.find(name)
        except ContactNotFoundError:
            return None

    def __str__(self) -> str:
        """
        Возвращает строку вида: Адресная книга: X контакта(ов)
        """
        count = len(self.contacts)
        return f"Адресная книга: {count} контакта"


# --- Тестирование (Пример работы из задания) ---

if __name__ == "__main__":
    pb = PhoneBook()
    
    # Успешное добавление контактов
    pb.add("Анна", "+7 900 123-45-67")
    pb.add("Иван", "8 800 555 35 35")
    
    # Проверка вызова InvalidPhoneError
    try:
        pb.add("Петр", "abc123")  # InvalidPhoneError
    except InvalidPhoneError as e:
        print(e)  # Ожидается: Некорректный номер телефона: "abc123"

    # Проверка методов поиска
    print(pb.find("Анна"))          # Ожидается: +7 900 123-45-67
    print(pb.find_safe("Незнакомец"))  # Ожидается: None

    # Проверка удаления и вызова ContactNotFoundError
    try:
        pb.delete("Незнакомец")   # ContactNotFoundError
    except ContactNotFoundError as e:
        print(e)  # Ожидается: Контакт "Незнакомец" не найден

    # Вывод информации о книге через __str__
    print(pb)  # Ожидается: Адресная книга: 2 контакта