Iteratory

Python SELF PL
Poziom 19 , Lekcja 4
Dostępny

5.1 Iterable i Iterator

Jak już wiesz, iteratory to obiekty, które implementują protokół iteratora, pozwalający sekwencyjnie pobierać elementy z kolekcji. Iteratory są szeroko wykorzystywane w Pythonie do iteracji elementów sekwencji takich jak listy, krotki i ciągi znaków.

Przyjrzymy się, jak działają iteratory i jak je używać.

Obiekt iterowalny (Iterable)

Aby można było przechodzić po obiekcie za pomocą pętli for, musi on być iterowalny – Iterable. Oznacza to, że nasz obiekt powinien implementować metodę __iter__(), która zwraca obiekt-iterator.

Obiekt-iterator (Iterator)

To specjalny obiekt, który ma funkcję __next__() do zwracania następnego elementu sekwencji. Gdy elementy się kończą, metoda __next__() wywołuje wyjątek StopIteration jako sygnał zakończenia iteracji.

Iterator powinien również implementować metodę __iter__(), która zwraca sam iterator.

Przykład z użyciem wbudowanych funkcji Python

W tym przykładzie lista numbers jest obiektem iterowalnym. Otrzymujemy iterator za pomocą funkcji iter() i używamy funkcji next() do iteracji elementów do momentu, gdy zostanie wywołany wyjątek StopIteration.


# Obiekt iterowalny
numbers = [1, 2, 3, 4, 5]
            
# Pobieranie iteratora z obiektu iterowalnego
iterator = iter(numbers)
            
# Używanie iteratora do iteracji elementów
try:
    while True:
        number = next(iterator)
        print(number)
except StopIteration:
    pass
        

Dokładnie to samo dzieje się, gdy piszesz kod typu:


# Obiekt iterowalny
numbers = [1, 2, 3, 4, 5]
            
for number in numbers:
    print(number)
        

5.2 Istota iteratora

Iterator to pewien obiekt, który pomaga nam sekwencyjnie przejść przez grupę elementów. Istnieje wiele różnych implementacji. Napiszmy własną klasę, w której zaimplementujemy wszystkie wymagania, które stawia się przed iteratorem.

Krok 1. Na początek stwórzmy własną klasę

Niech zwraca po kolei liczby od start do end


class MyIterator:
    def __init__(self, start, end):
        self.current = start
        self.end = end
        

Krok 2. Obsługa funkcji __iter__

Teraz musimy dodać funkcję __iter__, która będzie zwracać obiekt-iterator, u którego będzie wywoływana funkcja __next()__. Będziemy zwracać referencję do naszego własnego obiektu – to nie jest zabronione.


class MyIterator:
    def __init__(self, start, end):
        self.current = start
        self.end = end
        
    def __iter__(self):
        return self
        

Krok 3. Obsługa funkcji __next__

Teraz trzeba dodać do naszego obiektu iteratora funkcję __next__, która będzie zwracać kolejny element naszej listy. Po prostu będziemy używać zmiennej current:


def __next__(self):
    current = self.current
    self.current += 1
    return current
        

Krok 4. Zatrzymanie iteratora

Jeśli iterator zwrócił już wszystkie wartości, które planował, powinien wyrzucić wyjątek StopIteration. Dajmy małą poprawkę do naszej ostatniej funkcji:


def __next__(self):
    if self.current >= self.end: raise StopIteration
    current = self.current
    self.current += 1
    return current
        

Świetnie. Teraz możemy korzystać z naszego iteratora. Oto przykład całego naszego kodu:


class MyIterator:
    def __init__(self, start, end):
        self.current = start
        self.end = end
        
    def __iter__(self):
        return self
        
    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        current = self.current
        self.current += 1
        return current
        
# Tworzymy instancję własnego iteratora
my_iter = MyIterator(1, 5)
        
# Używamy iteratora do iteracji elementów
for num in my_iter:
    print(num)

5.3 Poprawny iterator

Co jest nie tak z iteratorem z poprzedniego przykładu? Tak, to jest iterator, działa, ale jest zbyt prymitywny. Nie można go używać do jednoczesnego przechodzenia po tej samej kolekcji elementów różnymi iteratorami.

Bardziej poprawne byłoby napisanie kodu, który zwracałby nie referencję do siebie w metodzie __iter__, lecz osobny obiekt, który dobrze wydzielałby wszystkie elementy.

Przykład:


class MyIterable:
    def __init__(self, data):
        self.data = data
    
    def __iter__(self):
        return MyIterator(self.data)
    
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        item = self.data[self.index]
        self.index += 1
        return item
    
# Użycie
my_iterable = MyIterable([1, 2, 3, 4])
for item in my_iterable:
    print(item)
    

W tym przykładzie mamy dwie klasy – do pierwszej przekazywana jest kolekcja, po której będziemy iterować. A druga – to właśnie iterator, który zwraca elementy kolekcji w metodzie next(). Jest również dość prosty, ale właśnie w ten sposób należy dodawać iteratory do swoich klas.

1
Опрос
Moduły i pakiety,  19 уровень,  4 лекция
недоступен
Moduły i pakiety
Moduły i pakiety
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION