Iteraatorid ja generaatorid¶
Iteratsioon on samade või sarnaste sündmuste kordumine. Klassikalise iteratsiooni näidiseks on tsüklid, näiteks:
for _ in range(10):
# Prints message 'How boring would life be without cycles?' 10 times to the console
print("How boring would life be without cycles?")
Ilma tsüklita peaksime kirjutama sama print()
meetodi manuaalselt 10 korda, mis on juba üsna vastik tegevus,
rääkimata olukordadest, kus korduste arvuks oleks näiteks 100, 1000 vms.
Generaatorid
Generaatorid on funktsioon, mis tagastab igal väljakutsumisel ühe jada liikme.
Erinevalt tavalistest funktsioonidest, mis tagastavad väärtusi võtmesõnaga return
,
tagastavad generaatorid järjest jada liikmeid võtmesõnaga yield
.
Kui generaatoril pole enam elemente, mida tagastada, siis tagastab ta StopIteration
erindi.
Generaatori järgmist elementi saab küsida funktsiooniga next(generator)
.
# Generator function
def square_range(n):
"""Generate first n square numbers"""
i = 0
while i < n:
yield i ** 2
i += 1
for i in square_range(5):
print(i, end=" ")
# output: 0 1 4 9 16
generator = square_range(3)
print(next(generator)) # 0
print(next(generator)) # 1
print(next(generator)) # 4
print(next(generator)) # StopIteration
Generaatori eeliseks listi ees on see, et iga järgnev element arvutatakse välja alles siis, kui seda on vaja. Seetõttu vajab generaator vähem arvuti mälu kui suur list. Toodud näites vajab generaator alla 0.000 MiB ruumi, kuid list vajab 0.137 MiB ruumi.
(venv) kristjan@kristjan-ThinkPad-T460s:~/PycharmProjects/pydoc_root$ python3 -m memory_profiler test_memory.py
Filename: test_memory.py
Line # Mem usage Increment Line Contents
================================================
1 14.605 MiB 14.605 MiB @profile
2 def f():
3 14.605 MiB 0.000 MiB num = 10_000_000
4 14.605 MiB 0.000 MiB range(num) # range is a generator
5 14.742 MiB 0.137 MiB list(range(num)) # save this range of numbers in a list
Samuti võib generaator olla lõpmatu. Näiteks:
def infinite_stream_of_true_and_false():
while True:
yield True
yield False
stream = infinite_stream_of_true_and_false()
print(next(stream)) # True
print(next(stream)) # False
print(next(stream)) # True
print(next(stream)) # False
print(next(stream)) # True
print(next(stream)) # False
Generaatori väljendid
Generaatori väljend näeb välja nagu list comprehension, lihtsalt ümarsulgudes.
# List comprehension
squared = [i*i for i in range(10)]
# We can read the stored data as many times as we like
print(squared) # --> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(squared) # --> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Generator expression
squared = (i*i for i in range(10))
print(squared) # --> <generator object <genexpr> at 0x01C13480>
print(list(squared)) # --> [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(list(squared)) # --> []
Iteraator klassina
Keerukamat iteraatorit võib kirjeldada ka klassina. Selleks tuleb defineerida __next__(self)
funktsioon.
class NewRange:
def __init__(self, start, end, step):
self.current = start
self.end = end
self.step = step
def __iter__(self):
return self
def __next__(self):
# If the limit is not yet reached
if self.current < self.end:
old_current = self.current
self.current += self.step
return old_current
else:
raise StopIteration
iterator = NewRange(1, 2, 0.25) # --> Creating an iterator object
print(next(iterator)) # --> 1
print(next(iterator)) # --> 1.25
print(next(iterator)) # --> 1.5
print(next(iterator)) # --> 1.75
# print(next(iterator)) # --> StopIteration
Rohkem lugemist