🌗 ← Índice
Data: 07/08/2026
Nível: Intermediário (Mês 2 — POO)
Você já usou métodos especiais sem perceber: len(lista), str(objeto), print(objeto), até objeto == outro — tudo isso funciona por causa dos métodos especiais (ou dunder methods, de double underscore). Eles são o "código por baixo dos panos" do Python, e hoje você vai aprender a controlá-los.
__str__ e __repr__ — representação em texto__len__ — fazer o len() funcionar__eq__, __lt__, __gt__ — comparações__add__, __sub__ — operadores aritméticos__contains__ — operador in__getitem__ — indexação obj[0]__call__ — objetos que viram funçõesSão métodos com dois underscores antes e depois (__nome__). O Python os chama automaticamente quando você usa certas operações. Você nunca chama obj.__str__() na prática — você usa str(obj) ou print(obj), e o Python faz a mágica.
# Sem métodos especiais:
class Conta:
def __init__(self, titular, saldo):
self.titular = titular
self.saldo = saldo
conta = Conta("Ana", 1500)
print(conta) # <__main__.Conta object at 0x7f...> 😕
💡 Esse texto estranho <__main__.Conta object at 0x...> é a representação padrão. Com métodos especiais, você controla isso.
__str__ e __repr__ — representação em texto__str__ → usado por print() e str() — leitura humana__repr__ → usado no console/REPL e em listas — representação técnica (idealmente, algo que recria o objeto)
class Conta:
def __init__(self, titular, saldo):
self.titular = titular
self.saldo = saldo
def __str__(self):
return f"Conta de {self.titular} — R$ {self.saldo:.2f}"
def __repr__(self):
return f"Conta('{self.titular}', {self.saldo})"
conta = Conta("Ana", 1500)
print(conta) # Conta de Ana — R$ 1500.00
print(str(conta)) # Conta de Ana — R$ 1500.00
print(repr(conta)) # Conta('Ana', 1500)
print([conta]) # [Conta('Ana', 1500)] ← usa __repr__
💡 Regra de ouro: implemente sempre o__repr__(é o que aparece em listas/erros). O__str__é opcional e é o que o usuário final vê.
__len__ — fazer o len() funcionar
class Carrinho:
def __init__(self):
self.itens = []
def adicionar(self, item):
self.itens.append(item)
def __len__(self):
return len(self.itens)
def __str__(self):
return f"Carrinho com {len(self)} itens"
c = Carrinho()
c.adicionar("Notebook")
c.adicionar("Mouse")
print(len(c)) # 2 ← sem __len__ daria TypeError
print(c) # Carrinho com 2 itens
__eq__, __lt__, __gt__Por padrão, == compara identidade (se é o MESMO objeto na memória). Com __eq__ você define o que significa "igual". Com __lt__/__gt__, o sorted() e < passam a funcionar.
class Produto:
def __init__(self, nome, preco):
self.nome = nome
self.preco = preco
def __eq__(self, outro):
return self.nome == outro.nome # mesmo nome = mesmo produto
def __lt__(self, outro):
return self.preco < outro.preco # ordenar por preço
def __repr__(self):
return f"{self.nome} (R$ {self.preco:.2f})"
p1 = Produto("Teclado", 250)
p2 = Produto("Teclado", 250) # objeto DIFERENTE, nome igual
p3 = Produto("Mouse", 120)
print(p1 == p2) # True (sem __eq__ seria False!)
print(sorted([p1, p3, Produto("Monitor", 900)])) # ordena por preço
💡 Com__eq__+__lt__, o Python deduz sozinho!=,>,<=,>=e a ordenação. Bônus: objetos viram chaves de dicionário se forem hashables.
__add__, __sub__
class Dinheiro:
def __init__(self, valor):
self.valor = valor
def __add__(self, outro):
return Dinheiro(self.valor + outro.valor)
def __sub__(self, outro):
return Dinheiro(self.valor - outro.valor)
def __str__(self):
return f"R$ {self.valor:.2f}"
salario = Dinheiro(5000)
aluguel = Dinheiro(1500)
resto = salario - aluguel
print(resto) # R$ 3500.00
print(salario + aluguel) # R$ 6500.00
💡 Existe o par para cada operador:__add__(+),__sub__(-),__mul__(*),__truediv__(/),__mod__(%)... e os reflexivos__radd__etc. O mesmo padrão serve pra todos.
__contains__ e __getitem__
class Biblioteca:
def __init__(self):
self.livros = ["Python", "POO", "Django"]
def __contains__(self, livro): # operador "in"
return livro in self.livros
def __getitem__(self, indice): # indexação obj[0]
return self.livros[indice]
def __len__(self):
return len(self.livros)
b = Biblioteca()
print("Python" in b) # True
print(b[1]) # POO
print(len(b)) # 3
__call__ — objeto que vira função
class Saudacao:
def __init__(self, nome):
self.nome = nome
def __call__(self, hora):
if hora < 12:
return f"Bom dia, {self.nome}!"
return f"Boa tarde, {self.nome}!"
oi = Saudacao("Hugo")
print(oi(10)) # Bom dia, Hugo! ← chamou o objeto como função
print(oi(15)) # Boa tarde, Hugo!
💡 Isso é o que o Python usa por baixo em decorators e frameworks (Flask, Django). Objetos __call__ guardam estado entre chamadas — uma função "com memória".
Pessoa (nome, idade) com __str__ ("Nome, X anos") e __repr__ (Pessoa('Nome', X)). Teste print(), repr() e print([pessoa]).FilaDeEspera com __len__ (quantas pessoas) e __contains__ (verificar se um nome está na fila).Livro (titulo, paginas) com __eq__ (mesmo título = igual) e __lt__ (ordena por páginas). Ordene uma lista com sorted().Crie a classe ContaCorrente completa usando métodos especiais:
__init__(titular, saldo) — atributos privados (_titular, _saldo)__str__ — "Conta de X — R$ Y"__eq__ — duas contas são iguais se o titular é o mesmo__lt__ — ordenar pelo saldo__add__ — somar duas contas retorna dinheiro total (não uma conta!)__contains__ — valor in conta verifica se o saldo cobre o valor500 in contaExtra: adicione __call__ — conta("depositar", 100) faz depósito e conta("sacar", 50) saca.
class Pessoa:
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
def __str__(self):
return f"{self.nome}, {self.idade} anos"
def __repr__(self):
return f"Pessoa('{self.nome}', {self.idade})"
p = Pessoa("Ana", 30)
print(p) # Ana, 30 anos
print(repr(p)) # Pessoa('Ana', 30)
print([p]) # [Pessoa('Ana', 30)]
class FilaDeEspera:
def __init__(self):
self.pessoas = []
def entrar(self, nome):
self.pessoas.append(nome)
def __len__(self):
return len(self.pessoas)
def __contains__(self, nome):
return nome in self.pessoas
f = FilaDeEspera()
f.entrar("Ana")
f.entrar("Bruno")
print(len(f)) # 2
print("Ana" in f) # True
print("Carlos" in f) # False
class Livro:
def __init__(self, titulo, paginas):
self.titulo = titulo
self.paginas = paginas
def __eq__(self, outro):
return self.titulo == outro.titulo
def __lt__(self, outro):
return self.paginas < outro.paginas
def __repr__(self):
return f"{self.titulo} ({self.paginas}p)"
livros = [Livro("Python", 300), Livro("POO", 150), Livro("Django", 500)]
print(sorted(livros)) # [POO (150p), Python (300p), Django (500p)]
print(Livro("Python", 300) == Livro("Python", 999)) # True (mesmo título)
class ContaCorrente:
def __init__(self, titular, saldo=0):
self._titular = titular
self._saldo = saldo
def __str__(self):
return f"Conta de {self._titular} — R$ {self._saldo:.2f}"
def __eq__(self, outra):
return self._titular == outra._titular
def __lt__(self, outra):
return self._saldo < outra._saldo
def __add__(self, outra):
return Dinheiro(self._saldo + outra._saldo)
def __contains__(self, valor):
return self._saldo >= valor
def __call__(self, acao, valor):
if acao == "depositar":
self._saldo += valor
elif acao == "sacar" and valor <= self._saldo:
self._saldo -= valor
return self._saldo
class Dinheiro:
def __init__(self, valor):
self.valor = valor
def __str__(self):
return f"R$ {self.valor:.2f}"
c1 = ContaCorrente("Ana", 3000)
c2 = ContaCorrente("Bruno", 1500)
c3 = ContaCorrente("Ana", 50) # mesmo titular que c1
print(c1 == c3) # True (mesmo titular)
print(sorted([c1, c2])) # ordena por saldo
print(c1 + c2) # R$ 4500.00 (soma vira Dinheiro)
print(2000 in c1) # True (saldo cobre 2000)
print(c1("depositar", 500)) # 3500
print(c1("sacar", 100)) # 3400
| Método | Ativa quando... | Exemplo |
|---|---|---|
__str__ | print(), str() | print(conta) |
__repr__ | Console, listas, erros | [conta] |
__len__ | len() | len(carrinho) |
__eq__ | ==, != | p1 == p2 |
__lt__ | <, sorted() | sorted(livros) |
__add__ | + | salario - aluguel |
__contains__ | in | "Python" in biblioteca |
__getitem__ | obj[0] | biblioteca[1] |
__call__ | Chamar o objeto | oi(10) |
Próxima aula: Propriedades — @property, getters/setters e validação de atributos. ✨