Métodos Dunder / Mágicos
Métodos com duplo underscore permitem que suas classes participem da sintaxe do Python - operadores, gerenciadores de contexto, iteração e formatação de strings. Implemente-os deliberadamente; os padrões nem sempre estão corretos para objetos de valor.
Receita
class Vector:
def __init__(self, x: float, y: float) -> None:
self.x = x
self.y = y
def __repr__(self) -> str:
return f"Vector({self.x}, {self.y})"
def __add__(self, other: "Vector") -> "Vector":
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other: object) -> bool:
if not isinstance(other, Vector):
return NotImplemented
return (self.x, self.y) == (other.x, other.y)Quando usar isso:
- Tipos de valor com
+,==ou ordenação naturais - Contêineres personalizados implementando
len,getitem - Tipos de recurso que precisam de limpeza com
with __repr__amigável para depuração em objetos de domínio
Exemplo de Trabalho
class Shelf:
def __init__(self, items: list[str]) -> None:
self._items = list(items)
def __len__(self) -> int:
return len(self._items)
def __getitem__(self, index: int) -> str:
return self._items[index]
def __contains__(self, item: object) -> bool:
return item in self._items
def __repr__(self) -> str:
return f"Shelf({self._items!r})"
class Money:
__slots__ = ("amount", "currency")
def __init__(self, amount: int, currency: str) -> None:
self.amount = amount
self.currency = currency
def __eq__(self, other: object) -> bool:
if not isinstance(other, Money):
return NotImplemented
return self.amount == other.amount and self.currency == other.currency
def __hash__(self) -> int:
return hash((self.amount, self.currency))
if __name__ == "__main__":
s = Shelf(["a", "b"])
print(len(s), "a" in s, s[0])
cache = {Money(100, "USD"): "ok"}
print(cache[Money(100, "USD")])O que isso demonstra:
- Protocolo de contêiner via
__len__,__getitem__,__contains__ - Igualdade de valor com
__eq__retornandoNotImplementedpara tipos desconhecidos - Correspondência de
__hash__permite o uso como chave de dicionário __repr__mostra uma dica de reconstrução orientada ao desenvolvedor
Mergulho Profundo
Como Funciona
- Modelo de dados - Python chama métodos dunder para operadores e builtins.
__init__vs__new__-__new__cria a instância;__init__inicializa. Raramente substitua__new__.- Comparações ricas -
__lt__etc. habilitamsortede@total_ordering. - Contrato de hash - Objetos iguais devem ter hashes iguais; o hash não deve mudar enquanto estiver em um dicionário/conjunto.
- Protocolo de iterador -
__iter__+__next__ou métodos geradores.
Dunders Comuns
| Método | Acionado por |
|---|---|
__repr__ | repr(), exibição interativa |
__str__ | str(), print() |
__eq__ | == |
__hash__ | hash(), chave de dicionário/conjunto |
__enter__/__exit__ | Declaração with |
Notas Python
from functools import total_ordering
@total_ordering
class Version:
def __init__(self, major: int, minor: int) -> None:
self.major = major
self.minor = minor
def __eq__(self, other: object) -> bool:
...
def __lt__(self, other: "Version") -> bool:
return (self.major, self.minor) < (other.major, other.minor)Armadilhas
- eq sem hash - Torna as instâncias não-hashable. Correção: Adicione
__hash__consistente ou usefrozendataclass. - Objetos mutáveis como chaves com hash - O hash não deve mudar após a inserção. Correção: Apenas objetos de valor imutáveis.
- repr retornando segredos do usuário - Vazamento de tokens em logs. Correção: Redija campos sensíveis.
- Retornando NotImplemented - Tipos errados devem retornar
NotImplemented, não False, para==. - Sobrecarga de tudo - Comportamento surpreendente do operador. Correção: Implemente apenas operações naturais.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
@dataclass | Boilerplate de repr/eq | Semântica de operador personalizada |
NamedTuple | Registros simples imutáveis | Necessidade de mutabilidade |
Biblioteca attrs | Ecossistema rico de validação | Restrição apenas à biblioteca padrão |
| Funções | Operação única em dados | Operadores melhoram a legibilidade |
FAQs
repr vs str?
__repr__ para desenvolvedores e logs - inequívoco. __str__ para mensagens do usuário final. Cadeia de fallback: str → repr.
Quando implementar __hash__?
Quando instâncias devem ser chaves de dicionário ou membros de conjunto e a igualdade é baseada em valor com campos imutáveis.
O que é NotImplemented?
Retorne da comparação rica quando o outro tipo for desconhecido - Python tenta a operação reversa no outro operando.
Preciso de __iter__ e __next__?
Implemente ambos para objetos iteradores. Classes iteráveis precisam apenas de __iter__ que produza um iterador.
Dataclass pode gerar dunders?
Sim - os sinalizadores eq, order, frozen, repr controlam os métodos gerados.
__bool__ vs __len__?
__bool__ define a veracidade. __len__ retornando 0 torna falso, a menos que __bool__ substitua.
Interação com __slots__?
Slots restringem atributos; alguns atributos dunder devem estar na lista __slots__ se usados.
Legibilidade da sobrecarga de operador?
Ótimo para vetores matemáticos, dinheiro, caminhos. Ruim para metáforas não relacionadas (+ em tipos não relacionados).
Caso de uso de __call__?
Torna instâncias chamáveis - padrão de functor, objetos decoradores, callbacks com estado.
Auxiliares de collections.abc?
Herde de MutableMapping etc. para herdar a lista de métodos necessários e orientação de documentação.
Relacionados
- dataclasses - dunders gerados
- Properties & Descriptors - acesso a atributos
- Imutabilidade & Hashability - regras de hash
- Context Managers - enter/exit
Versões da Pilha: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, e uv 0.6+.