O Que Você Vai Aprender
Nesta aula, você descobrirá os poderosos Métodos Mágicos (também conhecidos como "Dunder Methods") em Python. Eles são a chave para fazer seus objetos personalizados se comportarem como os tipos de dados nativos do Python, como strings, listas e números, permitindo uma interação mais intuitiva e "pythônica".
✨ O Que São Dunder Methods
Compreender a finalidade e a sintaxe dos métodos mágicos.
🖨️ __str__ e __repr__
Aprender a controlar a representação de string de seus objetos para usuários e desenvolvedores.
📏 __len__ e __eq__
Implementar a funcionalidade de comprimento e comparação de igualdade para seus objetos.
🎯 Aplicações Práticas
Ver exemplos práticos de como esses métodos tornam o código mais legível e robusto.
Conceito: Métodos Mágicos (Dunder Methods)
Em Python, os "métodos mágicos" (também conhecidos como "métodos dunder" por causa do "double underscore", ou "duplo sublinhado", em seus nomes, como `__init__`, `__str__`) são métodos especiais predefinidos que você pode usar para adicionar "mágica" às suas classes.
Como Funcionam
Esses métodos são chamados automaticamente pelo interpretador Python em resposta a certas operações. Por exemplo:
- Quando você usa a função `print()` em um objeto, o Python tenta chamar o método `__str__` desse objeto.
- Quando você usa a função `len()` em um objeto, o Python tenta chamar o método `__len__`.
- Quando você usa o operador `==` para comparar dois objetos, o Python tenta chamar o método `__eq__`.
Ao implementar esses métodos em suas classes, você pode definir como seus objetos se comportam em relação a essas operações embutidas, tornando suas classes mais intuitivas e alinhadas com o "estilo Python".
__str__ e __repr__: Representações de String
Esses dois métodos dunder são cruciais para controlar como seus objetos são representados como strings.
`__str__` (Para o Usuário)
Define a representação de string "informal" e legível por humanos de um objeto. É o que é retornado por `print(obj)`.
# Exemplo: __str__
class Livro:
def __init__(self, titulo, autor):
self.titulo = titulo
self.autor = autor
def __str__(self):
# Representação legível para o usuário
return f'"{self.titulo}" por {self.autor}'
# Uso:
# livro1 = Livro("O Pequeno Príncipe", "Antoine de Saint-Exupéry")
# print(livro1) # Chama __str__
# Saída: "O Pequeno Príncipe" por Antoine de Saint-Exupéry
`__repr__` (Para o Desenvolvedor)
Define a representação de string "oficial" de um objeto, que deve ser inequívoca e, se possível, permitir que o objeto seja recriado a partir dela. É o que é mostrado ao inspecionar um objeto no console ou em listas/dicionários.
# Exemplo: __repr__
class Livro:
def __init__(self, titulo, autor):
self.titulo = titulo
self.autor = autor
def __repr__(self):
# Representação para desenvolvedor
return f'Livro(titulo="{self.titulo}", autor="{self.autor}")'
# Uso:
# livro2 = Livro("1984", "George Orwell")
# print(repr(livro2)) # Chama __repr__ explicitamente
# Saída: Livro(titulo="1984", autor="George Orwell")
#
# Se __str__ não estiver definido, print() chamará __repr__.
__len__: Definindo o Comprimento
Ao implementar o método dunder `__len__`, você permite que a função embutida `len()` funcione com instâncias de sua classe, retornando o "comprimento" lógico do seu objeto.
Código Python para `__len__`
# Exemplo: __len__
class MinhaListaPersonalizada:
def __init__(self, elementos):
self.elementos = list(elementos) # Garante que é uma lista
def adicionar(self, elemento):
self.elementos.append(elemento)
def __len__(self):
# Define o comprimento do objeto como o comprimento da lista interna
return len(self.elementos)
# Uso:
# minha_lista = MinhaListaPersonalizada([1, 2, 3])
# print(f"Comprimento da lista: {len(minha_lista)}") # Chama __len__
# Saída: Comprimento da lista: 3
# minha_lista.adicionar(4)
# print(f"Novo comprimento: {len(minha_lista)}") # Chama __len__ novamente
# Saída: Novo comprimento: 4
__eq__: Definindo a Igualdade
O método dunder `__eq__` é usado para definir o comportamento do operador de igualdade (`==`) entre dois objetos da sua classe. Por padrão, `==` compara a identidade (se são o mesmo objeto na memória), mas `__eq__` permite que você defina o que significa "ser igual" para seus objetos.
Código Python para `__eq__`
# Exemplo: __eq__
class Ponto:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, outro):
# Verifica se 'outro' é uma instância de Ponto
if not isinstance(outro, Ponto):
return NotImplemented # Indica que a comparação não é suportada
# Define igualdade com base nos atributos x e y
return self.x == outro.x and self.y == outro.y
def __str__(self):
return f"Ponto({self.x}, {self.y})"
# Uso:
# p1 = Ponto(1, 2)
# p2 = Ponto(1, 2)
# p3 = Ponto(3, 4)
# print(f"p1 == p2: {p1 == p2}") # Chama __eq__
# Saída: p1 == p2: True
# print(f"p1 == p3: {p1 == p3}") # Chama __eq__
# Saída: p1 == p3: False
# p4 = Ponto(1, 2) # Mesmo valor, mas outro objeto na memória
# print(f"p1 is p4: {p1 is p4}") # Compara identidade, Saída: False
# print(f"p1 == 'não um ponto': {p1 == 'não um ponto'}") # Saída: False ou erro, dependendo da implementação
Simulador Interativo: Métodos Mágicos
Experimente os métodos mágicos com um objeto `Produto` simulado em JavaScript. Veja como ele se comporta quando você tenta imprimi-lo, obter seu comprimento ou compará-lo com outro produto.
Código Python das Classes Simuladas
# Classe Produto com métodos mágicos (simulada em JS)
class Produto:
def __init__(self, id, nome, preco, quantidade):
self.id = id
self.nome = nome
self.preco = preco
self.quantidade = quantidade
def __str__(self):
return f"{self.nome} (ID: {self.id}) - R${self.preco:.2f} [{self.quantidade} em estoque]"
def __repr__(self):
return f"Produto(id={self.id}, nome='{self.nome}', preco={self.preco}, quantidade={self.quantidade})"
def __len__(self):
return self.quantidade
def __eq__(self, outro):
if not isinstance(outro, Produto):
return NotImplemented
return self.id == outro.id and self.nome == outro.nome
Crie e Manipule Produtos
Produto 1
Produto 2
Ações nos Produtos
Desafios para Continuar
Agora é sua vez! Implemente esses métodos mágicos em suas próprias classes Python para entender melhor seu funcionamento.
-
✓
1. Classe `Playlist` com `__len__`, `__str__` e `__repr__`
Crie uma classe `Musica` com `titulo` e `artista`. Em seguida, crie uma classe `Playlist` que contém uma lista de objetos `Musica`. Implemente:
- `__len__`: Retorna o número de músicas na playlist.
- `__str__`: Retorna uma string amigável para o usuário, listando o título da playlist e o número de músicas.
- `__repr__`: Retorna uma string que permite recriar o objeto `Playlist`.
# Exemplo de Estrutura: class Musica: def __init__(self, titulo, artista): self.titulo = titulo self.artista = artista def __repr__(self): return f"Musica(titulo='{self.titulo}', artista='{self.artista}')" class Playlist: def __init__(self, nome, musicas=None): self.nome = nome self.musicas = [] if musicas is None else list(musicas) def adicionar_musica(self, musica): self.musicas.append(musica) def __len__(self): return len(self.musicas) def __str__(self): return f"Playlist '{self.nome}' com {len(self)} músicas." def __repr__(self): musicas_repr = ', '.join(repr(m) for m in self.musicas) return f"Playlist(nome='{self.nome}', musicas=[{musicas_repr}])" # Teste: # musica1 = Musica("Bohemian Rhapsody", "Queen") # musica2 = Musica("Stairway to Heaven", "Led Zeppelin") # minha_playlist = Playlist("Rock Clássico", [musica1]) # minha_playlist.adicionar_musica(musica2) # print(minha_playlist) # print(len(minha_playlist)) # print(repr(minha_playlist))
-
✓
2. Classe `Coordenada` com `__eq__`
Crie uma classe `Coordenada` com atributos `x` e `y`. Implemente o método `__eq__` para que duas instâncias de `Coordenada` sejam consideradas iguais se seus atributos `x` e `y` forem iguais. Adicione também um `__str__` para uma boa representação visual.
# Exemplo de Estrutura: class Coordenada: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"({self.x}, {self.y})" def __eq__(self, outro): if not isinstance(outro, Coordenada): return NotImplemented return self.x == outro.x and self.y == outro.y # Teste: # c1 = Coordenada(10, 20) # c2 = Coordenada(10, 20) # c3 = Coordenada(5, 15) # print(f"c1 == c2: {c1 == c2}") # print(f"c1 == c3: {c1 == c3}")