Desafio Python
Lista Personalizada com Operadores
Enunciado
Crie uma classe MinhaLista que encapsula uma lista de números. Implemente __len__ para retornar o tamanho, __iter__ para permitir iteração, __add__ para concatenar duas listas (retornando uma nova MinhaLista), __getitem__ para acessar elementos por índice e __repr__ para representação. Teste com operações de len(), iteração, concatenação e acesso por índice.
Requisitos
- A classe deve ter um construtor que aceita um iterável e armazena uma lista interna.
__len__retorna o número de elementos.__iter__retorna um iterador sobre os elementos.__add__aceita outraMinhaListae retorna uma novaMinhaListacom os elementos concatenados.__getitem__permite acessar elementos por índice inteiro.__repr__retorna uma string no formatoMinhaLista([1, 2, 3]).
Código inicial
class MinhaLista:
def __init__(self, iteravel):
self._dados = list(iteravel)
# Implemente os métodos especiais
# Teste
ml1 = MinhaLista([1, 2, 3])
ml2 = MinhaLista([4, 5])
print(len(ml1)) # 3
print(list(ml1)) # [1, 2, 3]
print(ml1[1]) # 2
ml3 = ml1 + ml2
print(ml3) # MinhaLista([1, 2, 3, 4, 5])
Saída esperada
3
[1, 2, 3]
2
MinhaLista([1, 2, 3, 4, 5])
Ver dica
Para __add__, verifique se o outro operando é MinhaLista e retorne NotImplemented caso contrário. __getitem__ pode delegar para a lista interna.
Mostrar solução
class MinhaLista:
def __init__(self, iteravel):
self._dados = list(iteravel)
def __len__(self):
return len(self._dados)
def __iter__(self):
return iter(self._dados)
def __add__(self, other):
if not isinstance(other, MinhaLista):
return NotImplemented
return MinhaLista(self._dados + other._dados)
def __getitem__(self, index):
return self._dados[index]
def __repr__(self):
return f"MinhaLista({self._dados!r})"
ml1 = MinhaLista([1, 2, 3])
ml2 = MinhaLista([4, 5])
print(len(ml1))
print(list(ml1))
print(ml1[1])
ml3 = ml1 + ml2
print(ml3)
Passo a passo
__init__converte o iterável recebido em uma lista e armazena em_dados.__len__delega paralen(self._dados).__iter__retorna um iterador sobre_dadosusandoiter().__add__verifica seotheréMinhaLista; se não, retornaNotImplemented. Caso contrário, cria uma novaMinhaListacom a concatenação das listas internas.__getitem__permite acesso indexado delegando para_dados[index].__repr__usa!rpara representar a lista interna de forma não ambígua.- Testamos com
len, iteração, acesso por índice e concatenação.
Por que funciona
Ao implementar __len__, len(ml1) funciona. __iter__ permite que list(ml1) e loops for percorram os elementos. __add__ possibilita o uso do operador +. __getitem__ habilita ml1[1]. __repr__ fornece uma representação legível. Esses métodos fazem com que MinhaLista se comporte como uma sequência nativa.
Erros comuns
- Esquecer de retornar
NotImplementedem__add__para tipos não suportados: se tentarml1 + 5, o Python lançaráTypeErrorem vez de tentar a operação reversa. - Implementar
__iter__retornandoself._dadosem vez deiter(self._dados): isso retornaria a lista, que é iterável, mas não é um iterador; no entanto, funciona emfor, mas não emnext(). O correto é retornar um iterador. - Não implementar
__getitem__e tentar indexar:TypeError: 'MinhaLista' object is not subscriptable. - Usar
__repr__sem!r: se os elementos forem strings, a representação pode ficar ambígua.
Outra forma de resolver
Poderíamos herdar de list e sobrescrever apenas __add__ e __repr__. Exemplo:
class MinhaLista(list):
def __add__(self, other):
if not isinstance(other, MinhaLista):
return NotImplemented
return MinhaLista(super().__add__(other))
def __repr__(self):
return f"MinhaLista({super().__repr__()})"
Isso é preferível quando queremos todas as funcionalidades de lista sem reimplementar tudo.
Saída esperada
3
[1, 2, 3]
2
MinhaLista([1, 2, 3, 4, 5])