8.3. Iterator

8.3.1. Rationale

  • EN: Iterator

  • PL: Iterator

  • Type: object

8.3.2. Use Cases

  • History (like browser history)

8.3.3. Problem

from dataclasses import dataclass, field


@dataclass
class BrowseHistory:
    _urls: list[str] = field(default_factory=list)

    def push(self, url: str) -> None:
        self._urls.append(url)

    def pop(self) -> str:
        self._urls.pop()

    def get_urls(self) -> list[str]:
        return self._urls


if __name__ == '__main__':
    history = BrowseHistory()
    history.push(url='https://a.example.com')
    history.push(url='https://b.example.com')
    history.push(url='https://c.example.com')

    for i in range(len(history.get_urls())):
        url = history.get_urls()[i]
        print(i)

8.3.4. Design

8.3.5. Implementation

../../_images/designpatterns-iterator-usecase.png
from dataclasses import dataclass, field


class Iterator:
    def has_next(self) -> bool:
        raise NotImplementedError

    def current(self) -> str:
        raise NotImplementedError

    def next(self) -> None:
        raise NotImplementedError


@dataclass
class BrowseHistory:
    _urls: list[str] = field(default_factory=list)

    def push(self, url: str) -> None:
        self._urls.append(url)

    def pop(self) -> str:
        self._urls.pop()

    def get_urls(self) -> list[str]:
        return self._urls

    def create_iterator(self) -> Iterator:
        return self.ListIterator(self)

    @dataclass
    class ListIterator(Iterator):
        __history: 'BrowseHistory'
        __index: int = 0

        def has_next(self) -> bool:
            return self.__index < len(history._urls)

        def current(self) -> str:
            return history._urls[self.__index]

        def next(self) -> None:
            self.__index += 1


if __name__ == '__main__':
    history = BrowseHistory()
    history.push(url='https://a.example.com')
    history.push(url='https://b.example.com')
    history.push(url='https://c.example.com')

    iterator = history.create_iterator()
    while iterator.has_next():
        url = iterator.current()
        print(url)
        iterator.next()
    # https://a.example.com
    # https://b.example.com
    # https://c.example.com

8.3.6. Example

class Crew:
    def __init__(self):
        self.members = list()

    def __iadd__(self, other):
        self.members.append(other)
        return self

    def __iter__(self):
        self._current = 0
        return self

    def __next__(self):
        if self._current >= len(self.members):
            raise StopIteration

        result = self.members[self._current]
        self._current += 1
        return result


crew = Crew()
crew += 'Mark Watney'
crew += 'Jose Jimenez'
crew += 'Melissa Lewis'

for member in crew:
    print(member)

# Mark Watney
# Jose Jimenez
# Melissa Lewis

8.3.7. Assignments