Last modified: March 29, 2023
This article is written in: 馃嚨馃嚤
Iteratory to fundamentalny koncept w j臋zyku Python, szczeg贸lnie w kontek艣cie p臋tli i struktur iterowalnych, takich jak listy, krotki czy zbiory. Python traktuje iteratory jako narz臋dzie do sukcesywnego uzyskiwania kolejnych element贸w z kolekcji danych w spos贸b zorganizowany, z mo偶liwo艣ci膮 zatrzymania i wznowienia procesu iteracji. W tej rozbudowanej wersji om贸wimy, jak dzia艂aj膮 iteratory, jak je tworzy膰, oraz jak s膮 wykorzystywane przez r贸偶ne mechanizmy Pythonowe, takie jak p臋tla for
oraz generatory.
W Pythonie obiekt jest iterowalny (ang. iterable), je艣li mo偶na po nim iterowa膰, tj. mo偶na sukcesywnie uzyskiwa膰 kolejne elementy. Obiekty te musz膮 implementowa膰 metod臋 __iter__()
, kt贸ra zwraca iterator. Iterator jest obiektem, kt贸ry realizuje protok贸艂 iteratora, to znaczy posiada metod臋 __next__()
, kt贸ra zwraca kolejny element w sekwencji.
Typowe struktury danych, takie jak listy, krotki, zbiory czy s艂owniki, s膮 iterowalne, co pozwala na u偶ycie p臋tli for
do iteracji nad ich elementami. W tle dzia艂a tutaj mechanizm oparty na iteratorach.
for elem in [1, 2, 3]:
print(elem)
Powy偶sza p臋tla for
w rzeczywisto艣ci wywo艂uje na li艣cie metod臋 __iter__()
, kt贸ra zwraca iterator. Iterator ten posiada metod臋 __next__()
, kt贸ra jest wywo艂ywana w ka偶dej iteracji w celu uzyskania kolejnego elementu listy.
Mo偶emy bezpo艣rednio pracowa膰 z iteratorem, korzystaj膮c z funkcji wbudowanej iter()
oraz metody next()
.
lista = [1, 2, 3]
iterator = iter(lista) # Tworzy iterator z listy
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# Kolejne wywo艂anie next()
wyrzuci wyj膮tek StopIteration
Kiedy iterator nie ma ju偶 kolejnych element贸w do zwr贸cenia, wywo艂anie next()
powoduje wygenerowanie wyj膮tku StopIteration
. Jest to sygna艂 dla p臋tli for
, aby zako艅czy膰 iteracj臋.
Protok贸艂 iteratora w Pythonie sk艂ada si臋 z dw贸ch kluczowych metod:
__iter__()
: Zwraca obiekt iteratora, zazwyczaj zwraca self
.__next__()
: Zwraca kolejny element w sekwencji, a gdy element贸w brak, generuje wyj膮tek StopIteration
.Zrozumienie tych metod jest kluczowe, je艣li chcemy definiowa膰 w艂asne iteratory.
Aby stworzy膰 w艂asny iterator, konieczne jest zdefiniowanie klasy z implementacj膮 metod __iter__()
i __next__()
. Przyk艂ad klasy b臋d膮cej iteratorem:
class MojaKolekcja:
def __init__(self):
self.elementy = [1, 2, 3]
self.indeks = 0
def __iter__(self):
return self
def __next__(self):
if self.indeks < len(self.elementy):
wynik = self.elementy[self.indeks]
self.indeks += 1
return wynik
else:
raise StopIteration
kolekcja = MojaKolekcja()
for elem in kolekcja:
print(elem)
Dzia艂anie powy偶szego przyk艂adu:
__iter__()
zwraca referencj臋 do samego siebie, co oznacza, 偶e instancja klasy jest tak偶e swoim w艂asnym iteratorem.__next__()
zwraca kolejny element z listy, a gdy wszystkie elementy zostan膮 wyczerpane, rzuca wyj膮tek StopIteration
, ko艅cz膮c iteracj臋.Iteratory w Pythonie opieraj膮 si臋 na koncepcji leniwej ewaluacji (ang. lazy evaluation). W przeciwie艅stwie do standardowych kolekcji, kt贸re przechowuj膮 wszystkie swoje elementy w pami臋ci, iterator nie generuje element贸w z g贸ry, ale produkuje je na 偶膮danie. Pozwala to na efektywne wykorzystanie pami臋ci, zw艂aszcza w przypadku du偶ych kolekcji danych lub niesko艅czonych sekwencji.
Matematycznie iteratory mo偶na postrzega膰 jako funkcje, kt贸re na ka偶dym kroku zwracaj膮 kolejny element z pewnej sekwencji, bez potrzeby przechowywania jej pe艂nej reprezentacji w pami臋ci. Dzi臋ki temu mo偶liwa jest np. praca z niesko艅czonymi sekwencjami.
Generatory s膮 specjalnym rodzajem iterator贸w, kt贸re s膮 definiowane za pomoc膮 funkcji wykorzystuj膮cych s艂owo kluczowe yield
. Generatory upraszczaj膮 proces tworzenia iterator贸w, automatycznie zarz膮dzaj膮c stanem iteracji i rzucaniem wyj膮tku StopIteration
po zako艅czeniu iteracji.
Przyk艂ad prostego generatora:
def generator_liczb():
for i in range(3):
yield i
for liczba in generator_liczb():
print(liczba)
Wyja艣nienie dzia艂ania generator贸w:
yield
zatrzymuje wykonanie funkcji i zwraca warto艣膰. Wznowienie nast臋puje w miejscu, w kt贸rym funkcja zosta艂a przerwana.StopIteration
w praktyceKa偶dy iterator po wyczerpaniu swoich element贸w rzuca wyj膮tek StopIteration
, kt贸ry informuje o zako艅czeniu iteracji. Jest to mechanizm kontrolny, kt贸ry pozwala p臋tlom for
automatycznie wykrywa膰, kiedy przesta膰 iterowa膰.
P臋tla for
automatycznie obs艂uguje wyj膮tek StopIteration
i przerywa iteracj臋, co czyni ten mechanizm intuicyjnym i 艂atwym w u偶yciu. Je艣li jednak korzystamy z iteracji r臋cznie za pomoc膮 next()
, musimy sami zadba膰 o odpowiedni膮 obs艂ug臋 wyj膮tku.
iterator = iter([1, 2, 3])
while True:
try:
element = next(iterator)
print(element)
except StopIteration:
break
__iter__()
i __next__()
.Generatory mo偶na r贸wnie偶 艂膮czy膰, tworz膮c bardziej z艂o偶one strumienie danych. Na przyk艂ad, mo偶na stworzy膰 generator, kt贸ry pobiera dane z innego generatora, przetwarza je i zwraca przetworzony wynik.
def podwoj_wartosci(generator):
for
wartosc in generator:
yield wartosc * 2
generator_liczb = (i for i in range(5))
podwojony_generator = podwoj_wartosci(generator_liczb)
for wartosc in podwojony_generator:
print(wartosc)
Powy偶szy przyk艂ad pokazuje, jak mo偶na tworzy膰 z艂o偶one przep艂ywy danych, gdzie generatory s膮 u偶ywane do transformacji danych w spos贸b p艂ynny i leniwy.