🌗 ← Índice

🐍 Python P13 — Métodos Especiais (Dunder Methods)


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.




🎯 O que vamos aprender


  • __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ções



  • 🧙 O que são métodos especiais


    Sã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
    



    ⚖️ Comparações: __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.



    ➕ Operadores aritméticos: __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".



    ✍️ Exercícios (15 min)


  • E1: Crie a classe Pessoa (nome, idade) com __str__ ("Nome, X anos") e __repr__ (Pessoa('Nome', X)). Teste print(), repr() e print([pessoa]).
  • E2: Crie FilaDeEspera com __len__ (quantas pessoas) e __contains__ (verificar se um nome está na fila).
  • E3: Crie Livro (titulo, paginas) com __eq__ (mesmo título = igual) e __lt__ (ordena por páginas). Ordene uma lista com sorted().



  • 🧪 Desafio (15 min)


    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 valor
  • Teste: crie 3 contas, ordene, compare, some os saldos e verifique 500 in conta

  • Extra: adicione __call__ — conta("depositar", 100) faz depósito e conta("sacar", 50) saca.




    ✅ Correção (10 min)


    Mostrar gabarito E1
    
    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)]
    

    Mostrar gabarito E2
    
    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
    

    Mostrar gabarito E3
    
    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)
    

    Mostrar gabarito Desafio
    
    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
    



    📌 Resumo da aula


    MétodoAtiva 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 objetooi(10)



    Próxima aula: Propriedades — @property, getters/setters e validação de atributos. ✨


    ← Voltar ao índice