Classes e objetos Python: o tutorial 2026
A maioria dos tutoriais Python OOP ensinam a sintaxe e ignoram o modelo mental. Este faz o oposto — você sairá com uma imagem clara do que uma classe é em tempo de execução, quando usar @dataclass em vez de escrever __init__ à mão e como self realmente funciona (não é mágica).
Abordaremos: a classe mínima viável de quatro linhas, por que self é um parâmetro, atributos de classe versus instância (o bug número 1 para iniciantes), os seis métodos dunder que você verá em todos os lugares, @dataclass como o padrão moderno e uma regra rígida sobre quando deixar as classes de lado e usar uma função simples.
O modelo mental: uma classe é uma fábrica
Uma classe em Python é uma fábrica que produz objetos. Cada vez que você chama Rect(3, 4), o Python cria um novo objeto com seu próprio armazenamento e o devolve para você.
Essa é toda a mecânica. A classe é o modelo; cada chamada Rect(...) é um carimbo do projeto na RAM nova.
self é apenas um parâmetro
A confusão número 1 para quem vem de JS ou Ruby: self em Python é explícito. O primeiro parâmetro de cada método é uma referência à instância que está sendo chamada.
Então c.bump() é um açúcar sintático para Counter.bump(c). Python passa a instância como o primeiro argumento. Você poderia chamá-lo de me, this ou foo — self é uma convenção, não uma palavra-chave. Fique com self (PEP 8) para que outros desenvolvedores Python possam ler seu código.
A armadilha do atributo classe versus instância
O bug mais comum do Python OOP em uma base de código para iniciantes:
items = [] no escopo da classe cria UMA lista compartilhada por cada instância. a.add("milk") altera-o, então b.items vê a mesma lista.
Regra: inicialize o estado mutável em `__init__`, não no escopo da classe.
Este bug foi enviado para produção mais vezes do que qualquer desenvolvedor Python admitiria. Fique atento a listas, dictos e conjuntos no escopo da classe.
Os seis métodos que você verá em todos os lugares
Os métodos Dunder (__name__) são ganchos de sobrecarga de operador do Python. Estes seis cobrem 95% do que você encontrará:
1. __init__(self, ...) — construtor
É executado quando o objeto é criado. Defina os atributos da instância aqui. Não retorne nada (Python impõe None).
2. __repr__(self) — string voltada para o desenvolvedor
Chamado por print(x) se __str__ estiver faltando, e sempre pelo REPL. Procure algo que você possa colar novamente no Python para recriar o objeto:
3. __eq__(self, other) — igualdade
Sem ele, Rect(3, 4) == Rect(3, 4) retorna False (o padrão é identidade). Compare campo por campo:
4. __hash__(self) — hashabilidade
Se você substituir __eq__, você também DEVE substituir __hash__ ou os conjuntos Python __hash__ = None (o objeto se torna não lavável). Mais simples: return hash((self.width, self.height)).
5. __len__(self) — para len(obj)
Faz len(cart) == 5 funcionar.
6. __iter__(self) — para for x in obj:
Retorne um iterador. yield dentro torna a classe iterável:
@dataclass — o padrão moderno
Se sua classe é principalmente um detentor de dados (três campos, um __init__, um __repr__, um __eq__), pare de escrever esse padrão. @dataclass gera tudo a partir de suas anotações de tipo:
Isso é __init__, __repr__ e __eq__, todos gerados. Adicione @dataclass(frozen=True) para tornar o objeto imutável E obter __hash__ gratuitamente.
Regra: procure `@dataclass` primeiro. Escreva uma classe rolada manualmente apenas quando precisar de métodos que não sejam acessadores de atributos.
__slots__ — otimização de memória (avançado)
Por padrão, cada instância carrega um __dict__ para que você possa adicionar atributos arbitrários. Trocas __slots__ que determinam uma estrutura fixa de nível C - menor consumo de RAM e acesso mais rápido aos atributos:
Use __slots__ quando: (a) você estiver criando milhões de instâncias e questões de RAM, ou (b) você quiser capturar atribuições de atributos com erros de digitação em tempo de execução. Caso contrário, ignore-o - ele desativa o pickle por padrão e quebra a herança múltipla de maneiras sutis.
Quando deixar as aulas em paz
Regra rígida: se sua classe tiver UM método mais `__init__`, provavelmente deveria ser uma função.
As classes ganham peso quando mantêm state em múltiplas chamadas de método ou quando implementam um protocolo bem definido (__iter__, __enter__, __eq__, etc.). A lógica one-shot é uma função.
Herança – use-a com moderação
A herança é real, mas usada em demasia. Em Python 2026, prefira composição — mantenha outro objeto como um atributo em vez de herdar dele:
Herde apenas quando você tiver um relacionamento genuíno "é um" E quiser substituir o comportamento. Um NamedRect que herda de Rect para adicionar um campo name é adequado. Construir uma hierarquia de herança de cinco níveis para uma classe de lógica de negócios é um código que você desvendará em 6 meses.
As classes são um dos recursos mais poderosos do Python e um dos mais fáceis de usar em excesso. O modelo mental que ajuda: uma classe é uma fábrica de objetos, self é um parâmetro simples, o estado mutável entra em __init__, procure @dataclass primeiro e volte para uma função sempre que a classe tiver menos de dois métodos portadores de estado.
Próxima etapa: 30 aulas práticas de OOP cobrindo aulas, @dataclass, herança versus composição e métodos dunder — com Python real rodando em seu navegador e um mentor de IA em cada etapa.