Generatori/iteratori¶
Generatorske funkcije¶
Spomenuli smo da for
petlja može “ići” ne samo po elementima liste (ili drugih sequence tipova) nego po bilo kojem iteratoru.
Iteratori su objekti koji definiraju niz omogućavanjem dohvaćanja sljedećeg elementa iz niza.
Time se definira niz jer se ponavljanjem te radnje prođu tj. dohvate svi elementi niza.
Iterator dakle pamti koliko elemenata je već prošao te sadrži recept kako dobiti sljedeći element.
Pisanje općenitog iteratora obično nije praktično.
Jednostavnije je koristiti generatorske funkcije.
Za razliku od običnih funkcija koje vraćaju jednu vrijednost (koja može biti lista ili tuple
) generatorske funkcije vraćaju više vrijednosti koristeći princip iteratora.
Sama generatorska funkcija izgleda isto kao obična osim što u sebi sadrži naredbu yield
.
yield
dohvaća sljedeću od više vrijednosti koje generatorska funkcija vraća.
Ona je slična naredbi return
jer kao i return
izlazi van funkcije i vrati vrijednost.
Međutim, kad se zatraži sljedeća vrijednost, generatorske funkcije će se nastaviti izvršavati dalje (a ne od početka) do sljedeće naredbe yield
. Pogledajmo primjere:
def f(): print("prije prvog yielda") yield 1 print("poslije prvog yielda") yield 2 print("poslije drugog yielda") yield 3 print("poslije trećeg yielda") return 4 i = f() print(i) print(1) print('next(i) daje', next(i)) print(2) print('next(i) daje', next(i)) print(3) print('next(i) daje', next(i)) try: print(4) print('next(i) daje', next(i)) except StopIteration as e: print('return je vratio',e.value)<generator object f at 0x106ec9480> 1 prije prvog yielda next(i) daje 1 2 poslije prvog yielda next(i) daje 2 3 poslije drugog yielda next(i) daje 3 4 poslije trećeg yielda return je vratio 4
Nakon što pozivatelj zatraži sljedeću vrijednost funkcija se izvršava do naredbe yield
.
Kad pozivatelj ponovno zatraži sljedeću vrijednost, izvršavanje funkcije se nastavlja od naredbe koja slijedi prethodnoj naredbi yield
do sljedeće yield
.
Ako pozivatelj zatraži sljedeću vrijednost a izvršavanje funkcije dođe do kraja ili do naredbe return
to izazove grešku StopIteration
.
Ta greška služi kao oznaka pozivatelju da nema više vrijednosti koje funkcija treba vratiti.
Obično je pozivatelj petlja:
def f(): print("prije prvog yielda") yield 1 print("poslije prvog yielda") yield 2 print("poslije drugog yielda") yield 3 print("poslije trećeg yielda") return 4 for x in f(): print('u petlji',x) print('.')prije prvog yielda u petlji 1 . poslije prvog yielda u petlji 2 . poslije drugog yielda u petlji 3 . poslije trećeg yielda
U gornjem primjeru petlja interno obrađuje grešku tako da nam ovdje detalji obrade pogrešaka nisu bitni. Nešto više detalja o try/except
je dano u Zadaća 8 (try ... except ... else, raise).
Generator comprehensions¶
Ako se for .. in
stavi u uglate zagrade rezultat je lista.
i = [x+100 for x in range(0,3)] print(i)[100, 101, 102]
Ako se for .. in
stavi u okrugle zagrade rezultat je iterator.
i = (x+100 for x in range(0,3)) print(i) print('next(i) daje', next(i)) print('next(i) daje', next(i)) print('next(i) daje', next(i))<generator object <genexpr> at 0x10d8d5390> next(i) daje 100 next(i) daje 101 next(i) daje 102
Pogledati primjere u odjeljku Zadatak 3b.
Više na:
Pretvaranje liste u iterator¶
Koristeći iter()
možemo prebaciti razne tipove u iterator npr. liste:
i = iter([1,2,3]) print(i) print('next(i) daje', next(i)) print('next(i) daje', next(i)) print('next(i) daje', next(i))<list_iterator object at 0x104ea4978> next(i) daje 1 next(i) daje 2 next(i) daje 3
Pretvaranje iteratora u listu¶
Koristeći list()
možemo prebaciti razne tipove u listu npr. iteratore:
def f(): yield 1 yield 2 yield 3 print(list(f()))[1, 2, 3]
Pridruživanje iteratora varijablama¶
Može biti koristan i ovaj način pozivanja generatorskih funkcija:
def f(): yield 1 yield 2 yield 3 a, b, c = f() print(a,b,c)1 2 3