Padrão de projeto – Iterator

O Iterator é um padrão de projeto comportamental que permite percorrer em elementos de uma coleção sem precisar saber como isso está acontecendo debaixo dos panos, independente de ser lista, pilha, árvore, etc.

Estrutura de dados

Dentre …


This content originally appeared on DEV Community and was authored by bug_elseif

O Iterator é um padrão de projeto comportamental que permite percorrer em elementos de uma coleção sem precisar saber como isso está acontecendo debaixo dos panos, independente de ser lista, pilha, árvore, etc.

Estrutura de dados

Dentre as estruturas citadas acima, vamos utilizar uma árvore como exemplo:

Árvores com setas indicando o tipo de acesso, em profundidade e em largura

Como podemos ver na imagem acima, a árvore pode ser acessada de diferentes maneiras, de acordo com o solicitado pelo cliente, podendo ter acesso em profundidade ou em largura, além da possibilidade de implementação de outros tipos de acesso aos elementos.

O Iterator é um padrão que extrai essas diferentes formas de percorrer uma árvore para um objeto chamado iterável, segue a imagem:

Arvores e UML contendo os métodos de busca em profundidade e em largura

Exemplo simples:
Implementar um mecanismo de busca, onde o cliente vai utilizar o método getBusca() sem saber com qual algoritmo o método foi desenvolvido - Bubble Sort, Merge Sort, Quick Sort...

Quando usar?

Esse padrão pode ser utilizado quando a complexidade da estrutura de dados não é interessante para o cliente. O acesso aos elementos é fornecido através de métodos simples, encapsulando os detalhes do algoritmo, trazendo mais proteção às coleções, as quais não serão manipuladas diretamente.

Outra vantagem de definir a responsabilidade de percorrer a estrutura de dados para o iterador, tirando ela de dentro da regra de negócio, se dá no tempo que os algoritmos não triviais levam para buscar um elemento. Também pelo fato de que o objeto iterador tem seu próprio estado de iteração, sendo possível mais de um objeto percorrer a mesma coleção em paralelo. Assim o código do cliente se torna mais limpo e focado em seus objetivos.

Iterator no Python

O python possui um módulo Iterator e Iterable, para criar os iteradores e os objetos iteráveis.

from collections.abc import Iterator, Iterable

Este módulo implementa os métodos __iter()__ e __next()__, os quais são necessários para um objeto ser iterável no Python.

import random

class RandomIterable:

def __iter__(self):
    return self
def __next__(self):
    if random.choice(["go", "go", "stop"]) == "stop":
        raise StopIteration
        # Sinaliza o fim da iteração
    return 1

Criando o iterator.py

Em um arquivo chamado iterator.py, criamos a classe InsertOrder, a qual vai definir a ordem que os elementos de uma coleção serão percorridos - implementamos a função __next()__ aqui.

class InsertOrder(Iterator):
_position: int = None
_reverse: bool = False

def __init__(self, collection: Iterable,
            reverse: bool = False) -> None:
    self._collection = collection
    self._reverse = reverse
    self._position = -1 if reverse else 0

def __next__(self):
    try:
        value = self._collection[self._position]
        self._position += -1 if self._reverse else 1
    except IndexError:
        raise StopIteration()
    return value
  • classe InsertOrder: possui atributos de posição do elemento e indicação se a coleção deve ser iterada na ordem normal ou reversa
  • função __init__: construtor da classe
  • função __next__: itera o objeto até não ter mais posições de índices, caindo na except, onde finaliza a iteração, aqui também verificamos se a busca vai ser normal ou reversa

No mesmo arquivo, criamos a classe WordsCollection, essa definirá um objeto iteravel, onde será implementada a segunda função necessária do Iterator, a __iter()__.

class WordsCollection(Iterable):
    def __init__(self, collection: list = []) -> None:
        self._collection = collection

    def __iter__(self) -> InsertOrder:
        return InsertOrder(self._collection)

    def get_reverse(self) -> InsertOrder:
        return InsertOrder(self._collection, True)

    def outras_formas(self) -> InsertOrder:
        # desenvolver outras formas de percorrer a coleção
        ...
        return InsertOrder(self.colletion)

    def add_item(self, item):
        self._collection.append(item)
  • classe WordsCollection: recebe a estrutura de dados, aqui usamos uma lista para facilitar o entendimento
  • função __init__: construtor da classe, ele vai receber a coleção para iterar
  • função __iter__: invoca a classe InsertOrder, passa a coleção como parâmetro e retorna um iterador
  • função get_reverse: invoca a classe InsertOrder, passa a coleção e a sinalização de ordem reversa como parâmetros e retorna um iterador
  • função outras_formas: um exemplo criado sem implementação, podendo criar diferentes regras de busca na mesma coleção, retornando também um iterator
  • função add_item: adiciona elementos à uma coleção

Criando o cliente.py

O cliente é quem vai utilizar o iterador, sem precisar saber sobre suas regras de busca, apenas passando a coleção e indicando o método.

def instance_collection(elements: list[str]) -> WordsCollection:
    collection = WordsCollection()
    for element in elements:
        collection.add_item(element)

    return collection
  • função instance_collection: instancia um objeto iterável, nesse caso uma lista de strings e retorna essa coleção

Executando o cliente.py

if __name__ == '__main__':
    elements = [
        'IFSC',
        'Programação',
        'Laboratório',
        'Professor',
        'Aluno'
        ]

    collection = instance_collection(elements)
  • variável elements: definimos uma lista de elementos
  • variável collection: chama a função para instanciar um objeto iterável, passando como parametro elements

Iterando pela collection de forma normal:

for e in collection.__iter__():
    print(e)

Saída:

IFSC
Programação
Laboratório
Professor
Aluno

Iterando pela collection de forma reversa:

for e in collection.get_reverse():
    print(e)

Saída:

Aluno
Professor
Laboratório
Programação
IFSC

Considerações

Com algumas adaptações no iterator.py, podemos fazer a iteração de diversas coleções e estruturas, sem a necessidade de alterar o código cliente.py, pois esse apenas consumirá o iterador.

Conhecendo mais sobre o módulo collections.abc e iterator do Python.

Pessoas que compartilham conhecimentos na comunidade: vcwild

Principal referência para desenvolvimento deste artigo.

Repositório com o código completo.

Enjoy!


This content originally appeared on DEV Community and was authored by bug_elseif


Print Share Comment Cite Upload Translate Updates
APA

bug_elseif | Sciencx (2022-07-11T04:06:40+00:00) Padrão de projeto – Iterator. Retrieved from https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/

MLA
" » Padrão de projeto – Iterator." bug_elseif | Sciencx - Monday July 11, 2022, https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/
HARVARD
bug_elseif | Sciencx Monday July 11, 2022 » Padrão de projeto – Iterator., viewed ,<https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/>
VANCOUVER
bug_elseif | Sciencx - » Padrão de projeto – Iterator. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/
CHICAGO
" » Padrão de projeto – Iterator." bug_elseif | Sciencx - Accessed . https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/
IEEE
" » Padrão de projeto – Iterator." bug_elseif | Sciencx [Online]. Available: https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/. [Accessed: ]
rf:citation
» Padrão de projeto – Iterator | bug_elseif | Sciencx | https://www.scien.cx/2022/07/11/padrao-de-projeto-iterator/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.