Métodos mágicos: str, repr, len, eq
1. Introdução aos Métodos Mágicos (Dunder Methods)
Métodos mágicos, também conhecidos como dunder methods (double underscore methods), são métodos especiais em Python que começam e terminam com dois underscores. Eles permitem que objetos definidos pelo usuário se comportem de forma semelhante aos tipos nativos do Python, respondendo a operações como print(), len(), ==, entre outras.
O termo "mágico" vem do fato de que esses métodos são chamados automaticamente pelo interpretador Python em contextos específicos, sem que o programador precise invocá-los explicitamente. Por exemplo, quando você usa print(objeto), o Python automaticamente procura pelo método __str__ do objeto.
Neste artigo, exploraremos quatro métodos mágicos fundamentais:
- __str__: representação amigável para usuários
- __repr__: representação técnica para desenvolvedores
- __len__: tamanho do objeto
- __eq__: comparação de igualdade
2. __str__ vs __repr__: Duas Faces da Representação
A principal diferença entre __str__ e __repr__ está no público-alvo:
__repr__: deve fornecer uma representação inequívoca e técnica do objeto, idealmente uma string que possa recriar o objeto. É usada para debugging e logs.__str__: deve fornecer uma representação legível e amigável para usuários finais. É usada porprint()ef-strings.
Na prática, quando você não define __str__, o Python usa __repr__ como fallback. Experimente no console interativo:
class Pessoa:
def __init__(self, nome, idade):
self.nome = nome
self.idade = idade
def __repr__(self):
return f"Pessoa('{self.nome}', {self.idade})"
def __str__(self):
return f"{self.nome}, {self.idade} anos"
p = Pessoa("Ana", 30)
print(p) # Saída: Ana, 30 anos
print(repr(p)) # Saída: Pessoa('Ana', 30)
3. Implementando __str__ na Prática
O método __str__ deve retornar uma string concisa e informativa. É ideal para classes que representam entidades do mundo real.
class Data:
def __init__(self, dia, mes, ano):
self.dia = dia
self.mes = mes
self.ano = ano
def __str__(self):
return f"{self.dia:02d}/{self.mes:02d}/{self.ano}"
data = Data(25, 12, 2024)
print(data) # Saída: 25/12/2024
Boas práticas:
- Retorne strings curtas (máximo 1-2 linhas)
- Evite informações técnicas internas
- Se o objeto for complexo, foque nos atributos mais relevantes
4. Implementando __repr__ na Prática
A regra de ouro para __repr__ é: a string retornada deve, idealmente, ser capaz de recriar o objeto quando passada para eval().
class Ponto2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Ponto2D({self.x}, {self.y})"
p = Ponto2D(3, 5)
print(repr(p)) # Saída: Ponto2D(3, 5)
# Recriando o objeto a partir da representação
p2 = eval(repr(p))
print(p2.x, p2.y) # Saída: 3 5
Cuidados com eval(): só use eval() com dados confiáveis. Em produção, prefira ast.literal_eval() ou use __repr__ apenas para debugging.
5. __len__: Tornando Objetos "Medíveis"
O método __len__ permite que objetos personalizados respondam à função len(). Deve retornar um inteiro não-negativo.
class Fila:
def __init__(self):
self._itens = []
def enfileirar(self, item):
self._itens.append(item)
def desenfileirar(self):
if self._itens:
return self._itens.pop(0)
raise IndexError("Fila vazia")
def __len__(self):
return len(self._itens)
fila = Fila()
fila.enfileirar("A")
fila.enfileirar("B")
fila.enfileirar("C")
print(len(fila)) # Saída: 3
# Integração com bool(): objetos vazios são False
if fila:
print("Fila não está vazia") # Esta linha executa
Importante: Python também usa __len__ indiretamente em bool(). Se __len__ retornar 0, o objeto é considerado False em contextos booleanos.
6. __eq__: Controlando a Igualdade entre Objetos
Por padrão, == compara identidade de objetos (mesmo que is). Para comparar por valor, implemente __eq__.
class Livro:
def __init__(self, titulo, autor, isbn):
self.titulo = titulo
self.autor = autor
self.isbn = isbn
def __eq__(self, outro):
if not isinstance(outro, Livro):
return NotImplemented
return self.isbn == outro.isbn
def __repr__(self):
return f"Livro('{self.titulo}', '{self.autor}', '{self.isbn}')"
livro1 = Livro("1984", "George Orwell", "978-8535902776")
livro2 = Livro("1984", "George Orwell", "978-8535902776")
livro3 = Livro("A Revolução dos Bichos", "George Orwell", "978-8535902950")
print(livro1 == livro2) # True (mesmo ISBN)
print(livro1 == livro3) # False (ISBNs diferentes)
print(livro1 is livro2) # False (objetos diferentes)
Cuidados importantes:
- Retorne NotImplemented (não False) quando comparar com tipos incompatíveis
- Se implementar __eq__, o Python 3 automaticamente fornece __ne__ (!=)
- Objetos mutáveis com __eq__ não devem ser usados como chaves de dicionário sem implementar __hash__
7. Interação e Boas Práticas com os Métodos Mágicos
Vamos criar uma classe que utiliza todos os quatro métodos de forma consistente:
class ContaBancaria:
def __init__(self, titular, saldo=0.0):
self.titular = titular
self._saldo = saldo
self._transacoes = []
def depositar(self, valor):
self._saldo += valor
self._transacoes.append(f"Depósito: +R${valor:.2f}")
def sacar(self, valor):
if valor > self._saldo:
raise ValueError("Saldo insuficiente")
self._saldo -= valor
self._transacoes.append(f"Saque: -R${valor:.2f}")
def __len__(self):
return len(self._transacoes)
def __eq__(self, outra):
if not isinstance(outra, ContaBancaria):
return NotImplemented
return self.titular == outra.titular and self._saldo == outra._saldo
def __repr__(self):
return f"ContaBancaria('{self.titular}', {self._saldo:.2f})"
def __str__(self):
return f"Conta de {self.titular} - Saldo: R${self._saldo:.2f}"
# Uso prático
conta1 = ContaBancaria("Maria", 1000)
conta1.depositar(500)
conta1.sacar(200)
print(str(conta1)) # Saída: Conta de Maria - Saldo: R$1300.00
print(repr(conta1)) # Saída: ContaBancaria('Maria', 1300.00)
print(len(conta1)) # Saída: 2 (duas transações)
conta2 = ContaBancaria("Maria", 1300)
print(conta1 == conta2) # True (mesmo titular e saldo)
Boas práticas:
- Mantenha consistência entre __str__, __repr__ e __eq__
- Use __len__ para objetos que representam coleções ou contêineres
- Sempre retorne NotImplemented em __eq__ para tipos incompatíveis
8. Conclusão e Próximos Passos
Os métodos mágicos __str__, __repr__, __len__ e __eq__ são ferramentas essenciais para criar classes Python que se integram naturalmente com a linguagem. Eles permitem que seus objetos personalizados:
- Sejam exibidos de forma amigável (__str__)
- Forneçam representações técnicas para debugging (__repr__)
- Respondam a len() (__len__)
- Sejam comparados por valor (__eq__)
Para aprofundar, explore tópicos como dataclasses, que geram automaticamente esses métodos, e outros métodos mágicos como __hash__, __lt__ e __getitem__. Dominar esses conceitos é fundamental para escrever código Python idiomático e elegante.
Referências
- Python Data Model: object.str — Documentação oficial sobre métodos de representação de objetos
- Python Data Model: object.repr — Documentação oficial sobre representação oficial de objetos
- Python Data Model: object.len — Documentação oficial sobre o método que define o tamanho do objeto
- Python Data Model: object.eq — Documentação oficial sobre comparação de igualdade entre objetos
- Real Python: Python’s Magic Methods — Tutorial abrangente sobre métodos mágicos em Python com exemplos práticos
- GeeksforGeeks: Dunder Methods in Python — Guia detalhado sobre métodos dunder com foco em str, repr e eq
- Python Tricks: Understanding Python's Magic Methods — Artigo técnico explicando o funcionamento interno dos métodos mágicos