Módulo 16 - Projeto final: agenda de contatos

Modelando a agenda com classes

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

O que você vai aprender

  • Criar a classe Contato com atributos nome, telefone e e-mail.
  • Escrever um método __str__ que mostra o contato de forma legível.
  • Criar a classe Agenda com uma lista interna de contatos.
  • Rodar o esqueleto do programa e confirmar que os objetos existem.

A classe Contato

Todo projeto de orientação a objetos começa perguntando quais coisas existem no problema. Na agenda, existem duas: um contato e a agenda que reúne os contatos. Comece pelo contato, porque a agenda depende dele. Um contato é um agrupamento de três valores que andam juntos: nome, telefone e e-mail. Guardar isso em três variáveis soltas seria frágil. Uma classe amarra os três em um objeto só, com nome próprio, e abre espaço para dar comportamento a ele depois.

class Contato:
    def __init__(self, nome, telefone, email=""):
        self.nome = nome
        self.telefone = telefone
        self.email = email

    def __str__(self):
        return f"{self.nome} | {self.telefone} | {self.email}"

A classe Contato: três atributos e um __str__ que a mostra em uma linha.

Duas escolhas merecem atenção. O parâmetro email recebe um valor padrão, a string vazia, o que o torna opcional: dá para criar um contato só com nome e telefone. E o método __str__ define como o contato aparece quando você o imprime. Sem ele, print(contato) mostraria algo técnico e inútil como <Contato object at 0x7f...>. Com ele, mostra Ana | 11999998888 | ana@exemplo.com, pronto para o usuário ler. É um detalhe pequeno que muda toda a experiência do programa.

A classe Agenda

Com o contato pronto, a agenda é quase natural. Ela é o objeto que guarda muitos contatos. A informação que ela precisa manter é uma coleção, e a coleção certa aqui é uma lista: os contatos têm ordem e podem ser adicionados e removidos. No __init__, a agenda começa com uma lista vazia. Ainda não há métodos para operar sobre ela; isso é o assunto da próxima aula. Por enquanto, a classe só reserva o lugar onde os contatos vão morar.

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


# Testando o esqueleto do programa
agenda = Agenda()
ana = Contato("Ana Souza", "11999998888", "ana@exemplo.com")
agenda.contatos.append(ana)

print(len(agenda.contatos))   # 1
print(agenda.contatos[0])     # Ana Souza | 11999998888 | ana@exemplo.com

A classe Agenda guarda uma lista de contatos. O teste no fim confirma o esqueleto.

Rode esse arquivo e observe o que acontece. Você cria uma agenda vazia, cria um contato, coloca o contato na lista da agenda com append e confere. O print(agenda.contatos[0]) mostra a linha bonita porque a classe Contato tem __str__. Neste momento, o programa já tem os dois tipos de objeto que importam. Falta dar à agenda métodos próprios, para que ninguém precise mexer diretamente na lista contatos de fora. Escrever agenda.contatos.append(ana) funciona, mas o certo é agenda.adicionar_contato(ana), e é o que vem a seguir.

Por que classes e não dicionários

Uma dúvida honesta: por que não guardar cada contato como um dicionário, algo como {"nome": "Ana", "telefone": "..."}? Funcionaria. A diferença é que a classe reúne, no mesmo lugar, os dados e o comportamento. O __str__ vive junto do contato; não é uma função solta que alguém pode esquecer de usar. Quando a agenda crescer e cada contato precisar de mais capacidades, como se transformar em dicionário para salvar, essas capacidades ficam todas na classe, organizadas. É a diferença entre uma pasta com etiqueta e papéis espalhados na mesa.

Contato como dicionário

  • Dados soltos: {"nome": ..., "telefone": ...}
  • Formatar para exibir é função externa
  • Fácil esquecer um campo ao criar
  • Comportamento espalhado pelo código

Contato como classe

  • Dados e comportamento no mesmo objeto
  • __str__ mora dentro do contato
  • O __init__ documenta os campos esperados
  • Tudo do contato fica em um lugar só

Isso não significa que dicionários sejam ruins. Para dados muito simples e passageiros, eles são ótimos. A regra prática é: quando um agrupamento de dados começa a ganhar comportamento e a se repetir pelo programa, promova-o a classe. O contato é exatamente esse caso. Curiosamente, quando formos salvar em JSON, cada contato vira um dicionário de novo, só que de propósito e por um instante. As duas ideias convivem, cada uma no seu momento.

Teste rápido

Qual é o papel do método __str__ na classe Contato?

Perguntas frequentes

Por que email tem um valor padrão e nome e telefone não?
Porque um contato quase sempre tem nome e telefone, mas nem sempre tem e-mail. Dar a email o valor padrão vazio o torna opcional: você pode criar Contato("Ana", "11999998888") sem informar e-mail. Parâmetros com padrão precisam vir depois dos sem padrão.
O que é self, que aparece em todos os métodos?
self é o próprio objeto, passado automaticamente para cada método. É por meio dele que o método acessa os atributos daquele objeto específico, como self.nome. Você viu isso a fundo no módulo de orientação a objetos; aqui ele reaparece na prática.
Preciso escrever __str__ em toda classe que eu criar?
Não em todas, mas é muito recomendado em classes cujos objetos você vai imprimir ou mostrar para o usuário, como o Contato. Ele deixa a saída legível de graça. Para classes internas que nunca aparecem na tela, é dispensável.
A lista contatos poderia ser um conjunto ou um dicionário?
Poderia, dependendo da necessidade. Um conjunto evitaria repetições, mas perderia a ordem. Um dicionário por nome aceleraria a busca. Escolhemos a lista por ser simples e preservar a ordem de inclusão, o que basta para uma agenda pequena e didática.
Por que criar a agenda vazia se ela ainda não faz nada?
Porque o projeto é incremental. Ter as duas classes existindo, mesmo simples, já é um programa que roda. Nas próximas aulas você acrescenta métodos a esse esqueleto sem precisar reescrever nada. Cada passo se apoia no anterior.
Onde devo salvar esse código no meu computador?
Crie um arquivo chamado agenda.py em uma pasta do projeto. É nele que o programa vai crescer a cada aula. Na aula de testes, você criará um segundo arquivo ao lado, para testar sem bagunçar o principal.

Fontes

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