Módulo 16 - Projeto final: agenda de contatos

Tratando erros com uma exceção própria

12 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026

O que você vai aprender

  • Criar a exceção própria ContatoNaoEncontradoError.
  • Fazer buscar_por_nome e remover levantarem a exceção quando preciso.
  • Validar o telefone com uma expressão regular.
  • Tratar o erro com try e except no ponto onde dá para responder ao usuário.

Uma exceção com nome próprio

Devolver None quando o contato não existe tem um defeito silencioso: quem chama pode esquecer de conferir e tentar usar o None como se fosse um contato, o que estoura um erro confuso lá adiante, longe da causa. A alternativa madura é falhar cedo e com clareza. Em vez de um None ambíguo, o método levanta uma exceção com nome que diz exatamente o que houve. Criar essa exceção é simples: uma classe vazia que herda de Exception já basta, e o nome carrega o significado.

class ContatoNaoEncontradoError(Exception):
    """Levantada quando um contato nao existe na agenda."""


class Agenda:
    def __init__(self):
        self.contatos = []

    def adicionar_contato(self, contato):
        self.contatos.append(contato)

    def listar(self):
        return list(self.contatos)

    def buscar_por_nome(self, nome):
        for contato in self.contatos:
            if nome.lower() in contato.nome.lower():
                return contato
        raise ContatoNaoEncontradoError(nome)

buscar_por_nome agora levanta a exceção em vez de devolver None.

A mudança em buscar_por_nome é pequena e profunda. Se o laço encontra um contato, devolve na hora. Se percorre tudo sem achar, em vez de terminar devolvendo None, executa raise ContatoNaoEncontradoError(nome). O raise interrompe o método e sinaliza o problema para quem chamou. Passar o nome procurado para a exceção guarda a informação útil: depois dá para saber qual nome falhou. Quem chama agora é obrigado a lidar com a possibilidade de erro, o que é bom, porque o erro deixou de passar despercebido.

Remover e validar o telefone

Com a busca levantando exceção, o método remover fica elegante: ele reaproveita buscar_por_nome. Se o contato existe, buscar_por_nome o devolve e remover o tira da lista. Se não existe, buscar_por_nome já levanta ContatoNaoEncontradoError, e remover nem precisa se preocupar, pois o erro sobe sozinho. É a vantagem de compor métodos: a regra de o que fazer quando não encontra mora em um lugar só. Aproveitamos também para validar o telefone, garantindo que só entrem números no formato esperado.

import re

TELEFONE_RE = re.compile(r"^\d{10,11}$")


def telefone_valido(telefone):
    return TELEFONE_RE.match(telefone) is not None


class Agenda:
    # ... init, adicionar_contato, listar e buscar_por_nome como antes ...

    def remover(self, nome):
        contato = self.buscar_por_nome(nome)
        self.contatos.remove(contato)

remover reaproveita a busca; telefone_valido confere o formato com regex.

A expressão regular \d{10,11} lê-se: de dez a onze dígitos, do começo ao fim do texto. As âncoras ^ e $ garantem que nada além dos números seja aceito, então 11999998888 passa e 11 9999-8888 não, porque tem espaço e traço. A função telefone_valido devolve True ou False, o que a torna fácil de usar em um if. Note que a validação não vive dentro da classe Agenda: ela é uma regra sobre o texto do telefone, útil antes mesmo de criar o contato. Deixá-la como função separada mantém cada responsabilidade no seu lugar.

Tratar o erro no lugar certo

Levantar a exceção é metade do trabalho; a outra metade é capturá-la onde dá para fazer algo útil. E esse lugar não é dentro dos métodos da agenda. A agenda não sabe conversar com o usuário; ela só conhece contatos. Quem conversa é o menu, que lê o que a pessoa digita e responde na tela. Então é no menu que o try e except entram: ele chama buscar_por_nome dentro de um try e, se vier a exceção, imprime uma mensagem amigável em vez de deixar o programa quebrar.

nome = input("Nome a buscar: ").strip()
try:
    contato = agenda.buscar_por_nome(nome)
    print(contato)
except ContatoNaoEncontradoError:
    print(f"Nao encontrei '{nome}'.")

O tratamento fica no menu, onde há como responder ao usuário.

Esse é um princípio que vale além da agenda: levante a exceção onde o problema acontece, mas trate onde há contexto para reagir. Os métodos da agenda levantam o erro porque é ali que se descobre que o contato não existe. O menu trata o erro porque é ali que se sabe o que dizer ao usuário. Se a agenda tratasse o erro sozinha, imprimindo algo, ela ficaria presa a esse jeito de responder e seria inútil em um programa com interface gráfica, por exemplo. Separar quem sinaliza de quem reage deixa cada parte reaproveitável.

Teste rápido

Por que o tratamento com try e except fica no menu, e não dentro de buscar_por_nome?

Perguntas frequentes

Por que criar uma exceção própria em vez de usar ValueError?
Porque o nome comunica a intenção. ContatoNaoEncontradoError diz exatamente o que aconteceu, enquanto ValueError é genérico. Um except ContatoNaoEncontradoError captura só esse caso, sem pegar por engano outros erros de valor que você talvez queira tratar de forma diferente.
A classe da exceção pode ficar vazia mesmo?
Sim. Herdando de Exception, ela já ganha todo o comportamento necessário. O corpo pode ser só uma docstring, que documenta quando o erro ocorre. O valor da exceção está no nome e na hierarquia, não em ter código dentro.
O que significam ^ e $ na expressão regular do telefone?
São âncoras: ^ marca o início do texto e $ o fim. Juntas, exigem que o texto inteiro seja só os dígitos, sem sobras. Sem elas, um telefone com letras no meio poderia passar, desde que tivesse dígitos suficientes em algum trecho.
Por que telefone_valido é função solta e não método da agenda?
Porque validar o formato de um telefone é uma regra sobre o texto, independente da agenda. Você pode querer validar antes mesmo de ter um contato ou uma agenda. Manter a função separada segue a ideia de cada responsabilidade no seu lugar.
remover realmente não precisa de try dentro dele?
Não. Ele chama buscar_por_nome, que levanta a exceção se o contato não existe. Como remover não sabe responder ao usuário, deixa a exceção subir para quem o chamou, o menu, que trata. É a mesma lógica de sempre: sinalizar em um lugar, tratar em outro.
Posso validar o e-mail com regex também?
Pode, e é um bom exercício. Validar e-mail perfeitamente é surpreendentemente difícil, então na prática costuma-se checar apenas se há um arroba com texto dos dois lados. Deixamos essa melhoria como sugestão na última aula para não alongar o projeto.

Fontes

Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.