Loeng 7: Generaator, iteraator, laisk itereerimine, lausendid yield ja yield from, sisseehitatud funktsioonid iter ja next, generaatoriavaldis ehk generaatori lĂŒhiesitus¶

Viimati uuendatud 13.10.2025.

KoodinÀited¶

1  Laisk itereerimine (lazy iteration)¶

Laisad iteraatorid lubavad töödelda suuri koguseid andmeid ilma neid korraga salvestades. Pythoni generaatorid ja iteraatorid vĂ€ljastavad tulemusi, vajadusel kasvĂ”i lĂ”pmatult palju kordi. Iteraat luuakse ja vĂ€ljastatakse ainult siis kui seda kĂŒsitakse.

Tihti polegi vĂ”imalust salvastada kĂ”iki vajaminevaid andmeid. Nt. arvuti parooli tĂ€hemĂ€rkide kombinatsioonide arv. TĂ€hemĂ€rkide ja numbrite kĂ”ikvĂ”imalike lĂ”pliku pikkusega permutatsioonide loendit pole pĂ”himĂ”tteliselt vĂ”imalik arvutisse salvestada. Neid saab aga ĂŒkshaaval luua.

2  Generaator, generaatorfunktsioon ja lausend yield¶

Generaator on spetsiaalne funktsioon, mille nimeruum sĂ€ilib vÀÀrtuse vĂ€ljastamisel. Lisaks sĂ€ilivad ka programmi voo andmed, neid andmeid kasutatakse jĂ€rgmiste tulemuste genereerimiseks. Generaator vĂ€ljastab vÀÀrtusi (iteraate) erilisel viisil $-$ itereerides neid laisalt. Generaatorfunktsioon vĂ€ljastab vÀÀrtusi ĂŒkshaaval salvestamata suuri andmestruktuure (nt. andmetĂŒĂŒbid: loend, korteeĆŸ, hulk, sĂ”naraamat, jne.) masina mĂ€llu.

Generaatorfunktsiooni defineerimiseks kasutame tavalist def blokki. VÀÀrtuse vÀljastamiseks kasutame generaatorfunktsiooni blokis lausendit yield. Tuletame meelde, et tavalises Pythoni funktskoonis vÀljastasime tulemusi kasutades lausendit return. Lausend return lÔpetab funktsiooni töö, lausend yield ei pruugi, see lÔpetab generaatori töö alles siis kui ettenÀhtud iteratsiooniprotsess on ammendunud.

Generaator on Pythoni objekt ĂŒle mille saab itereerida.

2.1  Generaatori defineerimine¶

Defineerime generaatori ja itereerime ĂŒle selle kuni see ammendub. Sama generaatorit saame kasutada mitmes iteratsiooni blokis.

In [1]:
def int_gen():  # Defineerime generaatorfunktsiooni.
    yield 1     # VĂ€ljastame laisalt arvu 1.
    yield 2     # VĂ€ljastame laisalt arvu 2.

for i in int_gen():  # Itereerin ĂŒle generaatori. NB! Kasuta suluge.
    print(i)         # NB! Operaator in loob taustal iteraatori, vt allpool.

for i in int_gen():  # Itereerime uuesti.
    print(' ', i, end='')
1
2
  1  2

Generaatorfunktsioonile saame edastada argumente ja argumentide vÀÀrtusi. Siin kehtivad kÔik meile teadaolevad def bloki reeglid ja vÔimalused.

In [2]:
def int_gen(start, stop):  # Argumentide lisamine.
    for i in range(start, stop):
        yield i

for i in int_gen(1, 3):  # Edastan argumentide vÀÀrtused.
    print(i)
1
2

NĂ€iteks kasutame *args tĂŒĂŒpi argumenti:

In [3]:
def str_gen(*args):  # *args-tĂŒĂŒpi argumentide lisamine.
    for i in args:
        yield i

for i in str_gen('Juulius', 'on', 'maskott.'):
    print(i, end=' ')
Juulius on maskott. 

Mis andmetĂŒĂŒbiga on tegu?

In [4]:
print(type(str_gen))
print(type(str_gen()))  # NB! Sulud.
<class 'function'>
<class 'generator'>

VÀljakutse str_gen vÀljastab (generaator)funktsiooni ja vÀljakutse str_gen() vÀljastab generaatori.
MÀrkus: Generaatorfunktsiooni saab kasutada koos kÔrgemat jÀrku funktsioonidega (KJF) ning see saab ise olla KJF.

2.2  Rekursiivne generaator¶

Nii nagu oli tavaliste Pythoni funktsioonidega saame ka siin kasutada rekursiooni. Rekursiivse funktsiooni bloki loomiseks kasutame loodava funktsiooni nime koodibloki sees.

In [5]:
def infinity(start):
    yield start
    for x in infinity(start + 1):  # Viitan generaatorile endale.
        yield x

for i in infinity(0):  # Itereerime ĂŒle rekursiivse generaatori.
    if i > 5:
        break  # Peatan kÀsitsi.
    print(i, end=' ')
0 1 2 3 4 5 

2.3  LÔpmatu generaator¶

Miski ei sega meil loomast lÔputut generaatorit, nÀiteks:

In [6]:
def seitse():
    while True:  # LĂ”pmatu tsĂŒkkel.
        yield 7

for i, j in enumerate(seitse()):
    if i > 2:
        break  # Peatan kÀsitsi.
    print(i * ' ', j)
 7
  7
   7
In [7]:
def arvud(n = 0):  # Vaikimisi alustame 0-st.
    while True:  # LĂ”pmatu tsĂŒkkel.
        yield n
        n += 1

for i in arvud():
    if i > 2:
        break  # Peatan kÀsitsi.
    print(i * ' ', i)
 0
  1
   2

2.4  Generaator ja iteraatortoega objektide konstruktorid¶

Pythoni sisseehitatud jadade andmetĂŒĂŒpide konstruktorfunktsioonid list, tuple, set ja dict itereerivad laisalt taustal ĂŒle generaatorite ja iteraatorite (vt. allpool). Peale itereerimist kasutatakse genereeritud andmeid vastava jada loomisel.

In [8]:
def int_gen():
    for i in [1, 2, 3]:
        yield i

list(int_gen())  # Taustal funk. list itereerib ĂŒle generaatori.
Out[8]:
[1, 2, 3]

MÀrkus: Funktsioon range kÀitub pealtnÀha vÀga sarnaselt generaatorile. Kuid pea meeles, et vahemik range pole tehniliselt generaator. Seega vahemik range vÔib teatud olukordades kÀituda ebaootuspÀraselt. Siin aga kÀitub see tÀpselt nagu eelmise koodiraku nÀide:

In [9]:
list(range(4))  # NB! Vahemik range kÀitub sarnaselt eelmisele rakule.
Out[9]:
[0, 1, 2, 3]

2.5  Generaator ja funktsioonid mis toetavad iteraatortoega objekte¶

Proovi ka muid sisseehitatud funktsioone mida saab rakendada iteraatortoega objektidele, nt. sum. Paljud Pythoni funktsioonid mida saab rakendada iteraatotorga objektidele toetavad generaatoreid. Generaator on iteraatortoega objekt.

In [10]:
def int_gen():
    for i in [1, 2, 3]:
        yield i

sum(int_gen())  # Taustal itereerib ĂŒle generaatori.
Out[10]:
6

2.6  Generaator ja lahtipakkimise operaator *¶

Lahtipakkimise operaator * toetab iteraatortoega objekte seega ka generaatorit. Operaator itereerib ĂŒle iteraatide leides vÀÀrtused mida tagastada. Operaator * ammendab generaatori.

In [11]:
def gen():
    yield 1
    yield 2

print(*gen())  # Itereerib, pakib lahti. Generaatori objekt on ammendatud.
print([*gen()])  # Pakin lahti listi.
1 2
[1, 2]

MÀrkus: SÔnastike lahtipakkimise operaator ** ei saa generaatorit lahti pakkida kuna vÀljakutse gen() tagastab generaatori mitte sÔnastiku.
Loomulikult saame laisalt vĂ€ljastada sĂ”nastikke ja tulemused ise kokku ĂŒhendada sĂ”nastikuks mida siis omakorda lahti pakkida.

In [12]:
def gen():
    yield {'key1': 1}  # VÀljastab sÔnastikke.
    yield {'key2': 2}

kokku = {k: v for d in gen() for k, v in d.items()}  # SÔnastiku avaldis.
print(type(kokku)) 
print({**kokku})  # Pakin lahti sÔnastikku.
print(kokku)
<class 'dict'>
{'key1': 1, 'key2': 2}
{'key1': 1, 'key2': 2}
In [13]:
kokku = {}
for d in gen():
    kokku |= d  # Dictionary union. SĂ”nastike ĂŒhend.

print(type(kokku))
print({**kokku})  # Pakin lahti sÔnastikku.
print(kokku)
<class 'dict'>
{'key1': 1, 'key2': 2}
{'key1': 1, 'key2': 2}

2.7  Funktsiooni print iteratiivne vÀljakutse ning lausendite yield ja return vÔrdlus¶

Keegi ei keela kirjutada funktsiooni vÔi generaatorfunktsiooni mis vÀljastab iteratiivselt funktsiooni print vÀljakutseid. Kirjutame generaatori:

In [14]:
def gen():
    for i in range(5):
        yield print(i, end=' ')
        
for tegevus in gen():
    tegevus
0 1 2 3 4 

Kood töötab ootuspÀraselt. Analoogne funktsiooniblokk kus lausend yield on asendatud lausendiga return on kujul:

In [15]:
def fun():
    for i in range(5):
        return print(i, end=' ')  # Lausendiga return me nii ei saanud teha aga siin on see lubatud.
        
fun()
0 

See kood ei ole vĂ”imeline kĂ”iki iteraatsioonitsĂŒkleid teostama kuna lausend return vĂ€ljub def blokist peale esimest tsĂŒklit.

3  Iteraator ehk generaatori objekt¶

Generaatori abil saame luua iteraatori ehk generaatori objekti. Generaatori abil loodud iteraatorit nimetatakse vahest ka laisaks iteraatoriks, kuna itereerimine on laisk. Iteraator on iteraatortoega. Eelnevalt olema kasutanud for ja while tsĂŒkleid, et itereerida ĂŒle iteraatortoega objektide. Lisaks saame itereerida ka ĂŒle iteraatorite.

Objekti loomiseks kasutame meelevaldset muutjanime, generaatorit ja omistusoperaaatorit =. SĂŒntaks on jĂ€rgmine:

<iteraatori nimi> = <generaatori nimi>(<argumendid kui on>)

Kui sulle tundub veider, et see rida loob iteraatori siis mÔtle jÀrgmistele nÀidetele:

In [16]:
n = int(3)

lst = list([1, 2, 3])

print(type(n))
print(type(lst))
<class 'int'>
<class 'list'>

Teatavasti on andmetĂŒĂŒbid defineeritud klassides, tĂ€isarvud klassis int ja loendid klassis list. Siin lĂ”ime klassi (konstruktorfunktsiooni) abil objekte. Klass on koht mis kirjeldab objekti, seega on see vĂ”imeline objekti looma. Sarnaselt klassile on generaator koht kus kirjeldatakse vĂ”i defineeritakse generaatori objekt ja seetĂ”ttu saame selle abil luua generaatori objekti ehk iteraatori.

Ühe generaatori abil saame luua meelevaldse arvu iteraatoreid. Iga uus iteraator on alglĂ€htestatud.

Generaator ja selle abil loodu iteraator on objektid ĂŒle mille saab itereerida.

3.1  Iteraatori defineerimine¶

Defineerime generaatori, loome selle abil iteraatori ehk generaatori objekti ja itereerime ĂŒle selle.
MĂ€rkus: Ära kasuta iteraatori nimena nime iter mis on sissehitatud objektide nimeruumi nimi.

In [17]:
def int_gen(): 
    yield 1 
    yield 2

iteraator = int_gen()  # NB! Loome iteraatori nimega 'iteraator'.
for i in iteraator:    # Itereerin ĂŒle iteraatori.
    print(i)

iteraator2 = int_gen()  # Uue iteraatori loomine kasutades sama generaatorit.
for i in iteraator2:    # Itereerin ĂŒle teise iteraatori.
    print(' ', i, end='')
1
2
  1  2

Generaatorfunktsioonile saame endiselt edastada argumente. Siin kehtivad kÔik meile teadaolevad def bloki reeglid ja vÔimalused.

In [18]:
def int_gen(start, stop):  # Argumentide edastamine.
    for i in range(start, stop):
        yield i

iter1 = int_gen(5, 8)  # Loome iteraatori.

for i in iter1:  # Itereerime ĂŒle iteraatori.
    print(i, end=' ')
5 6 7 

Kasutame *args tĂŒĂŒpi argumenti:

In [19]:
def str_gen(*args):  # *args-tĂŒĂŒpi argumentide lisamine.
    for i in args:
        yield i

iter1 = str_gen('Python', 'on', 'madu.')  # Lisa meelevaldne arv vÀÀrtusi.

for i in iter1:  # Itereerime ĂŒle iteraatori.
    print(i, end=' ')
Python on madu. 

Kontrollime mis objektiga on tegemist:

In [20]:
obj = str_gen(1, 2, 3)  # Loome generaatori objekti ehk iteraatori.
print(obj)  # Generaatori objekt on iteraator.
<generator object str_gen at 0x110722140>

3.2  Objektide loomisest laiemalt¶

3.2.1  Iteraatori loomine, lisainfo¶

Mida teeb omistuoperaator = tÀpsemalt kui me kasutame seda iteraatori loomisel? Kas toimub objektidele viitamine? Kas toimub andmetele viitamine? Uurime jÀrgmisi nÀiteid:

In [21]:
import types

def object_id(obj1, obj2):
    """VÔrdleb objektide id-sid ja nende andmeid."""
    print('Sama objekt:', obj1 is obj2)  # Erinevad objektid, erinevad funktsiooni id vÀÀrtused.
    if isinstance(obj1, types.GeneratorType):
        print('Samad andmed:', list(obj1) == list(obj2))  # Samad andmed.
    else:
        print('Samad andmed:', obj1 == obj2)  # Samad andmed.
    print('id(obj1) =', id(obj1))
    print('id(obj2) =', id(obj2))
    print('id(obj2) - id(obj1) =', id(obj2) - id(obj1))
    
def gen():
    yield 1 
    yield 2

obj1 = gen()  # Loome objekti.
obj2 = obj1  # Viitame objektile.

object_id(obj1, obj2)  # Sama objekt.
Sama objekt: True
Samad andmed: False
id(obj1) = 4570862992
id(obj2) = 4570862992
id(obj2) - id(obj1) = 0

Uue iteraatori loomine loob uue objekti koos uute andmetega. Sisuliselt lÔime koopia:

In [22]:
obj1 = gen()
obj2 = gen()

object_id(obj1, obj2)  # Uus objekt. obj2 on obj1 koopia.
Sama objekt: False
Samad andmed: True
id(obj1) = 4570864048
id(obj2) = 4570863696
id(obj2) - id(obj1) = -352

3.2.2  Objekti loomine kui koopia tegemine¶

Sama kopeerimise idee kehtib ka konstruktorfunktsioonide kasutamisel jÀrgmisel viisil:

In [23]:
obj1 = 333
obj2 = obj1  # Viitame objektile.

object_id(obj1, obj2)  # Sama objekt.
Sama objekt: True
Samad andmed: True
id(obj1) = 4572817584
id(obj2) = 4572817584
id(obj2) - id(obj1) = 0

Konstruktorfunktsioonid int jne. loovad uue koopia:

In [24]:
obj1 = 333
obj2 = int(333)  # Loon uue objekti. Ei kehti vÀikeste arvude puhul.

object_id(obj1, obj2)  # Tegi uue objekti (koopia).
Sama objekt: False
Samad andmed: True
id(obj1) = 4572815888
id(obj2) = 4572819280
id(obj2) - id(obj1) = 3392

Sarnane nĂ€ide kasutades loendi andmetĂŒĂŒpi:

In [25]:
obj1 = [1, 2, 3]
obj2 = obj1  # Viitame objektile.

object_id(obj1, obj2)  # Sama objekt.
Sama objekt: True
Samad andmed: True
id(obj1) = 4573038464
id(obj2) = 4573038464
id(obj2) - id(obj1) = 0

Kasutame konstruktorfunktsiooni list ja loome koopia:

In [26]:
obj1 = [1, 2, 3]
obj2 = list(obj1)  # Loon uue objekti.

object_id(obj1, obj2)  # Tegi koopia.
Sama objekt: False
Samad andmed: True
id(obj1) = 4573252224
id(obj2) = 4573010240
id(obj2) - id(obj1) = -241984

3.2.3  Jadade lÔikumine kui koopia tegemine¶

Lisaks eelmainitule tasub teada, et Pythonis kehtivad ka jÀrgmised indekseerimise ja lÔikudeks jagamise reeglid.

In [27]:
obj1 = [1, 2, 3]
obj2 = obj1[:]  # Loob uue objekti. Terve list.

object_id(obj1, obj2)  # Tegi koopia.
Sama objekt: False
Samad andmed: True
id(obj1) = 4573250432
id(obj2) = 4573328128
id(obj2) - id(obj1) = 77696
In [28]:
obj1 = [1, 2, 3]
obj2 = obj1[1:]  # Loob uue objekti. LÔik listist.

object_id(obj1, obj2)  # Teine objekt loodi.
Sama objekt: False
Samad andmed: False
id(obj1) = 4573252224
id(obj2) = 4573870016
id(obj2) - id(obj1) = 617792

Vahemikku range saab indekseerida, seega:

In [29]:
a = range(4)
b = a[:]  # Loob uue objekti.

object_id(a, b)  # Tegi koopia.
Sama objekt: False
Samad andmed: True
id(obj1) = 4573099664
id(obj2) = 4573238624
id(obj2) - id(obj1) = 138960
In [30]:
a = range(4)
b = a[1:]  # Loob uue objekti.

object_id(a, b)  # Teine objekt loodi.
Sama objekt: False
Samad andmed: False
id(obj1) = 4573242512
id(obj2) = 4573099664
id(obj2) - id(obj1) = -142848

3.3  Iteraator ja sisseehitatud funktsioon next¶

Iteraatori jÀrgmise vÀÀrtuse saab laisalt vÀljastada kasutades sisseehitatud funktsiooni next:

In [31]:
def int_gen():
    for i in [1, 2, 3, 4, 5]:
        yield i

g = int_gen()  # Loome iteraatori g.
next(g)  # Rakenda funktsiooni next iteraatorile.
Out[31]:
1
In [32]:
next(g)  # MĂ€letab itraatori olekut.
Out[32]:
2
In [33]:
next(g)  # JĂ€rgmine iteraat, jne.
Out[33]:
3

MÀrkus: Funktsiooni next pole mÔtet korduvalt ja otse rakendada generaatorile. Antud juhul esimene funktsioon next vÀljastab esimese iteraadi, teine vÀljakutse loob uue alglÀhtestatud generaatori ja tagastab jÀlle esimene tulemus (lisaks vt. selle dokumendi lÔppu).

In [34]:
print(next(int_gen()))
print(next(int_gen()))  # NB! Esimene iteraat.

g2 = int_gen()  # Loon uue iteraatori g2.

i = 1
while i < 4:
    print(next(g2), end=' ')
    i += 1
1
1
1 2 3 

3.4  Iteraatori ammendumine ja erisus StopIteration¶

Kui iteraator on ammendunud siis tĂ”statakse erisus StopIteration. Pythoni erisusestest ja veateadetest rÀÀgime tuleviku loengutes ĂŒksikasjalikumalt.

In [35]:
def int_gen():
    for i in [1, 2]:
        yield i

g = int_gen()

next(g)
Out[35]:
1
In [36]:
next(g)
Out[36]:
2
In [37]:
next(g)  # JÀrgmist vÀÀrtust pole olemas, iteraator ammendus.
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
Cell In[37], line 1
----> 1 next(g)

StopIteration: 

3.5  LÔpmatu iteraator¶

Eespool nÀgime lÔpmatute generaatorite nÀiteid. Taasesitame need siin ja loome nende pÔhjal ka iteraatorid:

In [38]:
def seitse():
    while True:  # LĂ”pmatu tsĂŒkkel.
        yield 7

s = seitse()  # Loon lÔpmatu iteraatori.

i = 0
while i < 3:  # Peatan itereerimise kÀsitsi.
    print(i * ' ', next(s))  # VÀÀrtuse loomine ja vÀljastamine.
    i += 1
 7
  7
   7
In [39]:
def arvud(n = 0):  # Vaikimisi alustame 0-st.
    while True:  # LĂ”pmatu tsĂŒkkel.
        yield n
        n += 1

num = arvud()  # Loome iteraatori num.

a = [next(num) for _ in range(5)]  # Loendi avaldis ja funktsioon next.
b = [next(num) for _ in range(5)]  # Sama iteraator, mÀletab viimast iteraatori olekut.
print(a, '-->', b)
[0, 1, 2, 3, 4] --> [5, 6, 7, 8, 9]

3.6  Iteraator ja iteraatortoega objektide konstruktorid¶

Eespool juba mainisime, et Pythoni sisseehitatud andmetĂŒĂŒpide kostruktorfunktsioonid list, tuple, set ja dict itereerivad taustal ĂŒle generaatorite ja iteraatorite. Kuna iteraator on iteraatortoega.

In [40]:
def int_gen():
    for i in [1, 2, 3]:
        yield i

g = int_gen()

print(list(g))  # Taustal funk. list itereerib ĂŒle iteraatori.
[1, 2, 3]

3.7  Itreaator ja funktsioonid mis toetavad iteraatortoega objekte¶

Paljud sisseehitatud funktsioonid toetavad iteraatortoega objektidele, nt. sum:

In [41]:
def int_gen():
    for i in [1, 2, 3]:
        yield i

g = int_gen()

print(sum(g))  # Taustal itereerib ĂŒle iteraatori.
6

3.8  Rekursiivne itereerimine¶

Eespool defineeritud rekursiivsed generaatorid loovad rekursiivseid iteraatoreid.

In [42]:
def infinity(start):
    yield start
    for x in infinity(start + 1):  # Viitan generaatorile endale.
        yield x

inf = infinity(0)

i = 0
while i < 3:  # Peatan itereerimise kÀsitsi.
    print(next(inf), end=' ')
    i += 1
0 1 2 

3.9  Sisseehitatud funktsioon iter¶

Funktsioon iter loob iteraatori kasutades iteraatortoega objekte ehk teisendab iteraatortoega objekte iteraatoriks. Iteraatori konstruktorfunktsioon. NĂ€ide kasutades iteraatortoega loendit:

In [43]:
lst = [1, 2, 3]  # Proovi ka teisi andmetĂŒĂŒpe.

iter_lst = iter(lst)  # Funktsiooni iter rakendamine loendile.

print(type(iter_lst))
print(next(iter_lst),
      next(iter_lst))  # VĂ€ljastan kaks esimest iteraati.
<class 'list_iterator'>
1 2
In [44]:
for i in iter_lst:
    print(i)  # MĂ€letab eelmist olekut.
3

NÀide kus kasutame iteraatortoega sÔnet:

In [45]:
st = 'Python'

iter_st = iter(st)

print(type(iter_st))
print(next(iter_st),
      next(iter_st))  # VĂ€ljastan kaks esimest iteraati.
<class 'str_ascii_iterator'>
P y
In [46]:
for i in iter_st:
    print(i, end=' ')  # MĂ€letab eelmist olekut.
t h o n 

3.10  Iteraator ja lahtipakkimise operaator *¶

Lahtipakkimise operaator * toetab iteraatortoega objekte seega ka iteraatorit. Operaator itereerib ĂŒle iteraatide leides vÀÀrtused mida tagastada. Operaator * ammendab iteraatori.

In [47]:
def gen():
    yield 1
    yield 2

obj = gen()
print(*obj)  # Itereerib, pakib lahti. Iteraator on ammendatud.

obj2 = gen()
print([*obj2])  # Pakin lahti listi.
1 2
[1, 2]

MÀrkus: SÔnastike lahtipakkimise operaator ** ei saa iteraatorit lahti pakkida kuna vÀljakutse obj tagastab generaatori objekti mitte sÔnastiku.
Loomulikult saame laisalt vĂ€ljastada sĂ”nastikke ja tulemused ise kokku ĂŒhendada sĂ”nastikuks mida siis omakorda lahti pakkida.

In [48]:
def gen():
    yield {'key1': 1}  # VÀljastab sÔnastikke.
    yield {'key2': 2}

obj = gen()

sr = {**next(obj), **next(obj)}  # Panen kÀsitsi kokku.
print(type(sr))
print({**sr})  # Pakin lahti sÔnastikku.
print(sr)
<class 'dict'>
{'key1': 1, 'key2': 2}
{'key1': 1, 'key2': 2}
In [49]:
obj = gen()

kokku = {k: v for d in obj for k, v in d.items()}  # SÔnastiku avaldis.
print(type(kokku))
print({**kokku})  # Pakin lahti sÔnastikku.
print(kokku)
<class 'dict'>
{'key1': 1, 'key2': 2}
{'key1': 1, 'key2': 2}
In [50]:
obj = gen()

kokku = {}
for d in obj:
    kokku |= d  # Dictionary union. SĂ”nastike ĂŒhend.

print(type(kokku))
print({**kokku})  # Pakin lahti sÔnastikku.
print(kokku)
<class 'dict'>
{'key1': 1, 'key2': 2}
{'key1': 1, 'key2': 2}

4  Lausend yield from¶

4.1  Alamgeneraator¶

Alltoodud nÀidet kasutame all pool lausendi yield from seletamiseks. Uurime nÀidet:

In [51]:
def generator1():
    for i in range(3):
        yield 'Gen 1:', i

def generator2():
    for i in range(3, 6):
        yield '\tGen 2:', i


def generator():
    for i in generator1():  # Itereerin ĂŒle generaatori, LEGB reegel.
        yield i
    for i in generator2():  # Itereerin ĂŒle generaatori, LEGB reegel.
        yield i


for i in generator():
    print(i[0], i[1])
Gen 1: 0
Gen 1: 1
Gen 1: 2
	Gen 2: 3
	Gen 2: 4
	Gen 2: 5

Lausend yield from vĂ”imaldab alamgeneraatori kasutust generaatori bloki sees. Lausend vĂ”imaldab eelmist nĂ€idet tunduvalt lĂŒhendada:

In [52]:
def generator():
    yield from generator1()
    yield from generator2()

for i in generator():
    print(i[0], i[1])
Gen 1: 0
Gen 1: 1
Gen 1: 2
	Gen 2: 3
	Gen 2: 4
	Gen 2: 5

Lausend yield from toetab ka iteraatorit:

In [53]:
obj1 = generator1()
obj2 = generator2()

def generator():
    yield from obj1
    yield from obj2

for i in generator():
    print(i[0], i[1])
Gen 1: 0
Gen 1: 1
Gen 1: 2
	Gen 2: 3
	Gen 2: 4
	Gen 2: 5

4.2  Rekursioon ja lausend yield from¶

Lausendit yield from kasutatakse rekursiivsetes generaatorites. Rekursioon ja lausend yield from:

In [54]:
def infty(start):
    yield start
    yield from infty(start + 1)  # Rekursiooni loomine.

for i in infty(0):  # Itereerime ĂŒle generaatori.
    if i < 3:  # Peatame itereerimise kÀsitsi.
        print(i)
    else:
        break
0
1
2
In [55]:
def recursive_generator(lst):  # Argumendina eeldame listi.
    if lst:  # Triviaalse objekti booli tÔevÀÀrtus on False.
        yield lst[0]
        yield from recursive_generator(lst[1:])  # Rekursiooni loomine.

for k in recursive_generator([6, 2, 5]):
    print(k)
6
2
5

5  KÔrgemat jÀrku generaatorfunktsioon¶

Generaatori defineerimisel saame argumendina edastada funktsioone vÔi vÀljastada ehk yield-da funktsioone. Seega saame defineerida kÔrgemat jÀrku generaatorfunktsioone (KJGF). TÀpselt nii nagu saime seda teha tavaliste Pythoni KJF defineerimisel.

5.1  NÀide: Logistilise kujutuse itereerimine¶

Logistiline kujutus on antud kujul:

$$ x_{n+1} = r x_n (1 − x_n), $$

kus $r$ on kontrollparameeter omades vÀÀrtusi vahemikus $[0, 4]$, $n \in \mathbb{Z}$ on itereerimisindeks ja iteraat $x_n$ saab omada vÀÀrtusi vahemikus $[0, 1]$.

Kasutame ĂŒlaltoodud kujutuse funktsiooni kujul:

$$ f(x) = r x (1 − x), $$

generaatorfunktsiooni argumendi vÀÀrtusena ning leiame kujutuse vÔi kujutise iteraadid $x_{n+1} = f(x_n)$ ehk logistilise kujutise.

Viide: https://en.wikipedia.org/wiki/Logistic_map

In [56]:
def iterate_func(func, x):  # KJGF.
    while True:  # LĂ”pmatu tsĂŒkkel.
        yield x
        x = func(x)  # Rakendame funktsiooni func x-le ja kirjutame x vÀÀrtuse ĂŒle.

r = 3.7
f = lambda x: r*x*(1 - x)
iterates_xn = iterate_func(f, 0.6)  # Loome iteraatori.

for _ in range(5):  # Peatan itereerimise kÀsitsi.
    print(next(iterates_xn))
0.6
0.8880000000000001
0.3679871999999997
0.8605186963537916
0.44409719744364157

Sama nÀide, siin salvestame leitud vÀÀrtused loendisse:

In [57]:
x = iterate_func(f, 0.6)  # Uus iteraator.

lst = []
for _ in range(5):  # Peatan itereerimise kÀsitsi.
    lst.append(next(x))  # Lisan iteraadid algselt tĂŒhja listi.
    
print(lst)
[0.6, 0.8880000000000001, 0.3679871999999997, 0.8605186963537916, 0.44409719744364157]

6  Generaatoravaldis ehk generaatori lĂŒhiesitus (generator comprehension)¶

Eelmisel nĂ€dalal tutvustatud for-in avaldis (list comprehension, set comprehension, dictionary comprehension) lĂ”i listi, hulga vĂ”i sĂ”naraamatu mis omakorda hoiab kĂ”iki loodud elemente masina mĂ€lus. Nagu juba eespool mainitud, generaator ja ka selle allpool tutvustatud lĂŒhiesitus ei loo suurt mĂ€lustruktuuri vaid vĂ€ljastab tulemused laisalt ehk ĂŒhekaupa ja vastavalt vajadusele.

6.1  Generaatoravaldise defineerimine¶

Generaatoravaldist defineerime kasutades sĂŒntaksis ĂŒmaraid sulge. See on ka pĂ”hjus miks eelmine nĂ€dal ei defineeritud korteeĆŸiavaldist (tuple comprehension). KorteeĆŸiavaldist pole olemas kuna see defineerib generaatori.

6.1.1  Kasutades for-in avaldist¶

Nii nagu eelmise nÀdala loengus kus filtreerisime liste, hulkasid ja sÔnaraamatuid, saame ka generaatoravaldistes kasutada for-in avaldist. NÀiteks loome generaaatori mis genereerib laisalt ruute:

In [58]:
(i**2 for i in range(4)) 
Out[58]:
<generator object <genexpr> at 0x110734860>
In [59]:
print(type(i**2 for i in range(4)))

ruudud = (i**2 for i in range(4))
print(type(ruudud))
print(ruudud)  # Generaatori objekt ehk iteraator.
<class 'generator'>
<class 'generator'>
<generator object <genexpr> at 0x11013bc60>
In [60]:
list(ruudud)  # Transleerime iteraadid listi.
Out[60]:
[0, 1, 4, 9]
In [61]:
rd = (i**2 for i in range(3))  # Anname nime generaatorile.

for i in rd:  # NB! Itereerin ĂŒle generaatori.
    print(i)
0
1
4

6.1.2  Kasutades for-in-if avaldist¶

Nii nagu eelmise nÀdala loengus kus filtreerisime liste, hulkasid ja sÔnaraamatuid, saame ka generaatoravaldistes kasutada for-in-if avaldist. NÀiteks loome generaaatori mis genereerib laisalt ainult paarituid ruute:

In [62]:
ptud_ruudud = (i**2 for i in range(7) if i % 2)  # VÔrdvÀÀrne tingimusega i % 2 == 1.

for i in ptud_ruudud:
    print(i)
1
9
25

6.1.3  Kasutades pesastatud for-in avaldisi¶

Pesastatud for-in avaldise kasutamine generaatoravaldises, vt. vrd. eelmise nÀdala materjale:

In [63]:
nested_gen = ((n, m) for n in range(2) for m in range(10, 12))

for j in nested_gen:
    print(j)
(0, 10)
(0, 11)
(1, 10)
(1, 11)

6.1.4  Kasutades if-else valikuavaldist koos for-in avaldisega¶

NÀide: Filtreeri laisalt loend a generaatoravaldise abil: Juhul kui arv on suurem vÔi vÔrdne $50$-ga liida sellele $1$, juhul kui arv on vÀiksem kui $50$ liida sellele $5$.

In [64]:
a = [22, 13, 45, 50, 98, 69, 43]
arvud = (x + 1 if x >= 50 else x + 5 for x in a)

for i in arvud:
    print(i, end=' ')
27 18 50 51 99 70 48 

6.1.5  Kasutades if-else valikuavaldist koos for-in-if avaldisega¶

NĂ€ide: Filtreeri laisalt loend a generaatoravaldise abil:

  1. Elimineeri paaritud arvud.
  2. Juhul kui paaris arv on suurem vÔi vÔrdne $50$-ga liida sellele $1$, juhul kui paaris arv on vÀiksem kui $50$ liida sellele $5$.
In [65]:
a = [22, 13, 45, 50, 98, 69, 43, 44, 1]
numbrid = (x + 1 if x >= 50 else x + 5 for x in a if x % 2 == 0)

for i in numbrid:
    print(i, end=' ')
27 51 99 49 

6.2  Generaatoravaldise ja funktsioonid mis toetavad iteraatortoega objekte¶

Nii nagu eespool nÀgime saame generaatorit ja ka selle poolt loodud iteraatorit kasutada sisseehitatud iteraatortoega funktsioonide argumentidena. Ka generaatoravaldist saab kasutada funktsiooni argumendina $-$ sellisel juhul pole isegi vaja kasutada kahte paari sulge.

NÀide: Leiame esimese miljoni mitte-negatiivse tÀisarvu ruutude summa kasutades sisseehitatud funktsiooni sum.

In [66]:
sum(i**2 for i in range(1_000_000))  # Funktsiooni sum sulud defineerivad generaatori.
Out[66]:
333332833333500000

6.3  Lausendid yield ja yield from ning avaldiste lĂŒhiesitused¶

Lausendit yield ei saa kasutada listi-, hulga-, sĂ”nastiku- ja generaatoravaldistes. SĂŒntaks kujul:

In [67]:
[yield i for i in range(5)]
  Cell In[67], line 1
    [yield i for i in range(5)]
     ^
SyntaxError: invalid syntax

on vigane. Lausend yield vĂ”ib esineda ainult generaatorfunktsiooni blokis — see tĂ€hendab def blokis. Sama kehtib ka lausendi yield from kohta:

In [68]:
[yield from (i for i in range(3))]
  Cell In[68], line 1
    [yield from (i for i in range(3))]
     ^
SyntaxError: invalid syntax

Lausendeid yield ja yield from saab kasutada generaatoravaldise ees ja ainult def bloki sees:

In [69]:
def gen_outer():
    yield (x for x in range(5))

print(list(*gen_outer()))
[0, 1, 2, 3, 4]
In [70]:
def gen_outer():
    yield from (x for x in range(5))

print(list(gen_outer()))
[0, 1, 2, 3, 4]

7  Liitgeneraator (pipelining generators, pipelining data)¶

Liitgeneraatori all mÔistame mitme generaatorite jÀrjestikku rakendamist iteraatortoega andmetele. Protsess sarnaneb matemaatikatunnist teada liitfunktsioonile kus seda funktsiooni rakendatakse laisalt jadalaadsetele andmetele.

Liitgeneraatori rakendamisel saame rÀÀkida nn. andmevoost (data pipeline). Andmevoog on jĂ€rjestikuste töötlemisetappide ahel, mille kĂ€igus andmed lĂ€bivad mitmeid samme ja muunduvad end algolekust lĂ”ppolekusse. Lihtsamalt öeldes on see toimingute jada, mis muudab andmeid. Andmeid muudetakse tavaliselt ĂŒhe kaupa.

Liitgeneraatori andmevoog koosneb tavaliselt jÀrgmistest sammudest:

  1. Andmete sisselugemine (data Ingestion) vÔi loomine: andmete lugemine erinevatest allikatest, nÀiteks API-dest, andmebaasidest vÔi failidest.
  2. Andmete muutmine (data Transformation) toimingute jadas: andmete puhastamine, filtreerimine, muutmine ja teisendamine.
  3. Andmete laadimine (data Loading): muudetud andmete salvestamine vÔi kasutamine.

7.1  Liitfunktsioon, meeldetuletus¶

Liitfunktsiooni saame andmevoo kontekstis defineerida jÀrgmiselt:

$$ f(x) \doteq \underbrace{f_{n} \Big( f_{n-1} \big(\ldots f_3 \big( f_2 (f_1 }_{n} ( \{ \overbrace{x_1, x_2, \ldots, x_{m-1}, x_m }^{m} \} \underbrace{) ) \big) \ldots \big) \Big)}_{n} $$

kus $n$ on kasutatud funktsioonide $f_i$ arv, kusjuures $i \in [1, 2, \ldots, n-1, n]$, ja kus $x = \{ x_1, x_2, \ldots, x_{m-1}, x_m \}$ on jada tĂŒĂŒpi Pythoni objekt, nt. hulk, kus $m$ on tĂ€isarv. Funktsiooni $f(x)$ nimetatakse liitfunktsiooniks. Andmevoo stsenaariumis rakendatakse funktsioone $f_i$ kĂ”igile jada elementidele $x_j$ kus $j \in [1, 2, \dots, m-1, m]$ ĂŒhe kaupa.

Liitfunktsiooni nÀide, reaalarvuline funktsioon on kujul:

$$ f(x) = \sqrt{\exp(x^2)}. $$

Funktsooni saab jagada kolemeks osaks:

$$ f_1(x) = x^2, $$

$$ f_2(x) = \exp(x), $$

$$ f_3(x) = \sqrt{x}. $$

7.2  Liitfunktsioon ja mitte-laisk iteratsioon¶

VÔrdluseks vaatama lihtsat nÀidet:

In [71]:
def fun1(x):
    return x * 2

def fun2(x):
    return x + 1

def liitfun(x):
    return fun2(fun1(x))  # Liitfunktsioon.

for i in [1, 2, 3]:  # Rakendame listi igale elemendile.
    print(liitfun(i))
3
5
7

See nÀide demonstreerib andmete voogu:

In [72]:
def fun1(x):
    l = []
    for i in x:
        l.append(i * 2)
    return l

def fun2(x):
    l = []
    for i in x:
        l.append(i + 1)
    return l

def liitfun(sisend):  # Andmed sisenevad ja voolavad lÀbi.
    tulem_1 = fun1(sisend)
    tulem_2 = fun2(tulem_1)
    return tulem_2


print(liitfun([1, 2, 3]))
[3, 5, 7]

Sama tulemuse saame kasutades lĂŒhemat kirjapilti:

In [73]:
def fun1(x):
    return [i * 2 for i in x]

def fun2(x):
    return [i + 1 for i in x]

def liitfun2(x):
    return fun2(fun1(x))

print(liitfun2([1, 2, 3]))
[3, 5, 7]

7.3  Liitgeneraator ja laisk iteratsioon¶

Siin kasutame generaatoreid ja spetsiaalset liitgeneraatori defineerimise sĂŒntaksit.
NB! Pane tĂ€hele, et andmevoog toimub nagu eespool kirjeldatud. Kaks funktsiooni muudavad andmeid ĂŒhe kaupa ja ammendavad kogu etteantud jada.

In [74]:
def gen1(x):  # NB! Eeldame, et x on jada.
    for i in x:
        yield i * 2

def gen2(x):  # NB! Eeldame, et x on jada.
    for i in x:
        yield i + 1


liit_iteraator = gen2(gen1([1, 2, 3]))  # Liitgeneraator. Generator pipelining.

for i in liit_iteraator:
    print(i)
3
5
7

Demonstreerime mis jÀrjekorras teostatakse tulemuste laisk leidmine. Kasutame funktsiooni print:

In [75]:
def gen1(x):  # NB! Eeldame, et x on jada.
    for i in x:
        print('Sisenes gen1')
        yield i * 2

def gen2(x):  # NB! Eeldame, et x on jada.
    for i in x:
        print('Sisenes gen2')
        yield i + 1


liit_iteraator = gen2(gen1([1, 2, 3]))  # Liitgeneraator. Generator pipelining.

for i in liit_iteraator:
    print('  ', i)
Sisenes gen1
Sisenes gen2
   3
Sisenes gen1
Sisenes gen2
   5
Sisenes gen1
Sisenes gen2
   7

NB! VĂ€ljakutse gen2(gen1([1, 2, 3])) teostatakse taustal erilisel viisil rakendades jadamisi generaatoreid gen1 ja siis gen2 listi elementidele ĂŒhekaupa.

Eelmise kahe koodiraku koodi saab loomulikult ka teisiti kirjutada, lĂŒhem kirjapilt:

In [76]:
sisend = [1, 2, 3]

gen1 = (i * 2 for i in sisend)
gen2 = (i + 1 for i in gen1)

for i in gen2:
    print(i)
3
5
7

Kasutades avaldiste pesastamist:

In [77]:
sisend = [1, 2, 3]

gen = (i + 1 for i in (i * 2 for i in sisend))

for i in gen:
    print(i)
3
5
7

Kasutades liitfunktsiooni generaatoriavaldise sees.

In [78]:
fun1 = lambda x: x * 2
fun2 = lambda x: x + 1
liitfun = lambda x: fun2(fun1(x))  # Liitfunktsioon.

sisend = [1, 2, 3]
gen = (liitfun(i) for i in sisend)

for i in gen:
    print(i)
3
5
7

7.4  NÀide: Moondatud Fibonacci jada¶

Leiame Fibonacci jada liikmete ruutude ruutjuured (nonsenss) kasutades generaatorite jÀrjest rakendamist. Vastuseks peab tulema jada ise. Fibonacci jada on kujul:

$$ 1 \quad 1 \quad 2 \quad 3 \quad 5 \quad 8 \quad 13 \quad 21 \quad 34 \quad 55 ~~ \ldots $$

Allolevas koodis genereeritakse (iteratiivselt, mitte-rekursiivselt) esimene Fibonacci jaga liige, siis rakendatakse sellele ruutu tÔstmine ja lÔpuks seda juuritakse. Peale esimese tulemuse vÀljastuse teostatakse sama protsess teise Fibonacci jada liikmega, jne.:

In [79]:
def fibonacci(n):
    x, y = 0, 1
    for _ in range(n):  # Leitakse ainult n jada liiget.
        x, y = y, x + y
        yield x

def ruudud(xid):  # NB! Eeldab iteraatortoega sisendit.
    for i in xid:
        yield i ** 2
        
def juured(xid):  # Eeldab iteraatortoega sisendit.
    for i in xid:
        yield int(i ** 0.5)

koos = juured(ruudud(fibonacci(10)))  # Liititeraatori loomine.

for i in koos:  # Laisk itereerimine ĂŒle iteraatori.
    print(i, end=' ')
1 1 2 3 5 8 13 21 34 55 

Generaatoreid saab jĂ€rjest rakendada ka funktsioonide argumendi sulgudes. NĂ€iteks Fibonacci jada kĂŒmne esimese liikme ruutude ja siis juurte summa on leitav nii:

In [80]:
sum(juured(ruudud(fibonacci(10))))
Out[80]:
143

Sarnane kood kasutades lĂŒhemat kirjapilti tĂ€nu generaatoriavaldisele:

In [81]:
# Meetod append tagastab triviaalse objekti (tĂŒhimĂ€rk None),
# seega operaator or tagastab state.pop(0) mis ka tagastab triviaalse objekti.
n = 10

olek = [1, 1]
fib = (olek.append(olek[0] + olek[1]) or olek.pop(0) for _ in range(n))
ruudud = (i ** 2 for i in fib)
juured = (int(i ** 0.5) for i in ruudud)

for i in juured:
    print(i, end=' ')
1 1 2 3 5 8 13 21 34 55 

Pesastamine lubab ridu veelgi kokku hoida, koodi loetavuse arvelt:

In [82]:
n = 10
x = [1, 1]
juured = (int(i**0.5) for i in (i**2 for i in (x.append(x[0] + x[1]) or x.pop(0) for _ in range(n))))

for i in juured:
    print(i, end=' ')
1 1 2 3 5 8 13 21 34 55 

8  Mis vahe on iteraatoril ja iteraatortoega objektil?¶

Erinevused mis puudutavad funktsiooni iter:

  • Iteraatortoega objektile saame mĂ”testatult rakendada funktsiooni iter ehk teisendada seda iteraatoriks.
  • Generaatorile pole tavaliselt mĂ”tet rakendada funktsiooni iter, tagastab iteraatori. Kasuta parem obj = gen().
  • Iteraatorile pole mĂ”tet rakendada funktsiooni iter, tagastab iseenda.

Erinevused mis puudutavad funktsiooni next:

  • Iteraatortoega objektile ei saa rakendada funktsiooni next, tĂ”statub erisus TypeError.
  • Iteraatorile saab rakendada funktsiooni next.
  • Generaatoritele pole tavaliselt mĂ”tet rakendada funktsiooni next, vĂ€ljastab alati esimese iteraadi.

Erinevused mis puudutavad itereeritavust (puuduvad):

  • Iteraatortoega objekti saab itereerida for- ja while-tsĂŒklis.
  • Generaatorit saab itereerida for- ja while-tsĂŒklis.
  • Iteraatorit saab itereerida for- ja while-tsĂŒklis.

Allolevates nÀidetes vastavad maagilistele meetoditele vÔi nn. dunder-meetoditele __iter__ ja __next__ meile tuttavad sisseehitatud funktsioonid iter ja next.

In [83]:
a = [1, 2, 3, 4]  # Iteraatortoega list.
print(type(a))
dir(a)  # Puudub meetod __next__ aga on meetod __iter__.
<class 'list'>
Out[83]:
['__add__',
 '__class__',
 '__class_getitem__',
 '__contains__',
 '__delattr__',
 '__delitem__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getitem__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__iadd__',
 '__imul__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__len__',
 '__lt__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__reversed__',
 '__rmul__',
 '__setattr__',
 '__setitem__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'append',
 'clear',
 'copy',
 'count',
 'extend',
 'index',
 'insert',
 'pop',
 'remove',
 'reverse',
 'sort']
In [84]:
def gen():  # Iteraatortoega generaator.
    yield 1

print(type(gen()))
dir(gen())  # Eksisteerivad meetodid __next__ ja __iter__.
<class 'generator'>
Out[84]:
['__class__',
 '__del__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__lt__',
 '__name__',
 '__ne__',
 '__new__',
 '__next__',
 '__qualname__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'gi_suspended',
 'gi_yieldfrom',
 'send',
 'throw']
In [85]:
iteraator = iter(a)  # Iteraator.

print(type(iteraator))
dir(iteraator)  # Eksisteerivad meetodid __next__ ja __iter__.
<class 'list_iterator'>
Out[85]:
['__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__iter__',
 '__le__',
 '__length_hint__',
 '__lt__',
 '__ne__',
 '__new__',
 '__next__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__setstate__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']

Kuna funktsioonile iter vastab dunder-meetod __iter__ saame funktsiooni iter vÀljakutse asendada meetodi rakendamisega:

In [86]:
a = [1, 2, 3, 4]  # Iteraatortoega list.
iteraator2 = a.__iter__()  # Ekvivalentne tulemus eelmises rakus nÀidatuga.

print(type(iteraator2))
<class 'list_iterator'>

Iteraatorile saame rakendada sisseehitatud funktsiooni next ja sellele vastavat dunder-meetodit __next__. Iteraatortoega objektile seevastu ei saa:

In [87]:
print(next(iteraator2))  # Esimene iteraat.
print(iteraator2.__next__())  # Teine iteraat.

for i in iteraator2:  # Laisk itereerimine, ĂŒlejÀÀnud iteraadid.
    print(' ', i)
1
2
  3
  4
In [88]:
next(a)  # Kasutan eespool loodud listi a.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[88], line 1
----> 1 next(a)

TypeError: 'list' object is not an iterator

















☻   ☻   ☻ Â