Blog JSystems - uwalniamy wiedzę!
Blog JSystems - uwalniamy wiedzę!

Generator jest funkcją która może zostać wstrzymana i wznowiona od miejsca w którym została wstrzymana. Generatory cechują się leniwą ewaluacją. Tworzą kolejne elementy dopiero w momencie odwołania się do generatora. Pozwala nam to wydajniej wykorzystywać pamięć operacyjną i zwracać z generatora nieskończoną liczbę elementów
Generatory nie tworzą całej zwracanej kolekcji od razu, tylko każda kolejna wartość jest generowana w momencie jej pobrania. To duża oszczędność dla pamięci, ponieważ w dowolnym momencie możemy przerwać pobieranie kolejnych wartości. Nie moglibysmy tego zrobić gdyby funkcja zwracała listę, choć sposób iteracji byłby taki sam. W przypadku generatorów w pamięci przechowywany jest tylko jeden aktualnie pobierany element a nie cała lista, co pozwala nam przetwarzać zbiory w zasadzie nieskończenie długie, czego nie moglibysmy zrobić w przypadku zwracania listy. Lista mogłaby przepełnić dostępną pamięć.
Z zagadnień podstawowych pamiętamy taką konstrukcję:
for x in range(10):
print(x)
Konstrukcja ta pozwalała na iterowanie po kolejnych elementach zwracanych przez range. Istnieje możliwość tworzenia własnych mechanizmów tego typu. Przyjrzyjmy się poniższemu kodowi:
def elementy():
yield 'element numer 1'
yield 'element numer 2'
yield 'element numer 3'
yield 'element numer 4'
for e in elementy():
print(e)
Powyżej widzimy bardzo prosty generator. Wynik jego działania przedstawia się następująco:
element numer 1
element numer 2
element numer 3
element numer 4
Moja funkcja "elementy" za pomocą słowa kluczowego "yield" podaje nam kolejne elementy. Słowo kluczowe „yield” odpowiada za przerwanie wykonania funkcji, zapisanie jej aktualnego stanu i zwrócenie kolejnej wartości. Jak widzimy w powyższym przykładzie, stan wykonania funkcji elementy był zapamiętywany i dlatego iterując po wyniku tej funkcji dostajemy kolejne podawane przez „yield” elementy.
W podobny sposób możemy utworzyć własną adaptację range podającą nam wartości co 10:
def myrange(n):
for x in range(n):
yield x*10
for x in myrange(10):
print(x)
Idąc tym tropem możemy budować bardziej złożone konstrukcje. Poniżej przykład generatora który podaje tyle potęg kolejnych liczb ile otrzyma przez argument:
def potegi2(n):
for x in range(1, n + 1):
yield pow(2, x)
for p in potegi2(5):
print(p)
Wynik działania na konsoli:
2
4
8
16
32
Nie zawsze chcemy przetworzyć cały zbiór jaki generator może nam zwrócić. Weźmy pod uwagę funkcję generującą która będzie nam zwracała kolejne wartości bez końca. W poniższym przypadku będą to kolejne dziesięci:
def dziesieci():
i=1
while True:
yield i*10
i+=1
Mogę teraz pojedynczo pobierać kolejne wartości korzystając z poniższej konstrukcji:
dz=dziesieci()
print( dz.__next__() )
print( dz.__next__() )
print( dz.__next__() )
Na konsoli dostaję dane:
10
20
30
Ten sam skutek mogę osiągnąć taką konstrukcją:
dz=dziesieci()
print(next(dz))
print(next(dz))
print(next(dz))
Generator może oddawać wartości różnych typów i generować je na różne sposoby, ważne by oddawać kolejne wartości za pomocą yield:
def poryRoku():
pory = ['styczeń', 'luty', 'marzec', 'kwiecień', 'maj', 'czerwiec', 'lipiec', 'sierpień', 'wrzesień', 'październik',
'listopad', 'grudzień']
for e in pory:
yield e
for p in poryRoku():
print(p)
Powyzej generator zwracający nam nazwy kolejnych miesięcy. Kolejny przykład to generator kolejnych liczb parzystych:
def parzyste(n):
for x in range(n + 1):
yield 2 * x
for p in parzyste(10):
print(p)
Generator kolejnych liter alfabetu:
def literki():
for x in range(97,123):
yield(chr(x))
for l in literki():
print(l)
Teraz nieco bardziej praktyczny przykład. Generator który czyta plik csv, rozbija każdą z linii csv na listę wg podanego przez argument rozdzielacza. Na potrzeby tego przykładu stworzyłem plik o nazwie "plik.csv" i umieściłem w nim następujące dane:
1;Artur
2;Krzysztof
3;Zenon
4;Marcin
5;Andrzej
Dodajemy generator i wywołujemy go:
def rozbijacz_csv(np,r):
plik=open(np,encoding='utf-8')
while True:
linia=plik.readline()
if not linia:
break
yield linia.strip().split(r)
rc=rozbijacz_csv('plik.csv',';')
print(next(rc))
print(next(rc))
Wynik na konsoli:
['1', 'Artur']
['2', 'Krzysztof']
Zwróć uwagę że nie użyłem konstrukcji typu "for linia in plik.readlines(): " tylko wczytuję kolejne linie jedna po drugiej. Robię tak dlatego, że "readlines()" wczytuje od razu całą zawartość pliku, co mogłoby skończyć się przepełnieniem pamięci w przypadku bardzo dużego pliku.
Ten kurs daje Ci solidne podstawy Pythona. Jeśli chcesz pójść dalej i pod okiem trenera dojść od pierwszej linijki kodu do własnego modelu uczenia maszynowego, sprawdź nasze pięciodniowe szkolenie Python od podstaw, przez analizę danych, do machine learning. Pierwsze trzy dni to Python w praktyce: typy i kolekcje, instrukcje warunkowe i pętle, funkcje, moduły i pakiety, pliki tekstowe, usługi sieciowe oraz praca z bazą PostgreSQL. Czwarty dzień to analiza danych w pandas (wczytywanie CSV, TSV i XLS, filtrowanie, agregacje, statystyki) i wizualizacja w Matplotlib, Seaborn oraz Plotly. Piąty dzień to uczenie maszynowe w scikit-learn: regresja liniowa i logistyczna, drzewa decyzyjne, las losowy, XGBoost, walidacja predykcji, dobór hiperparametrów i przeciwdziałanie przeuczeniu. Szkolenie ma terminy gwarantowane, czyli odbywa się niezależnie od wielkości grupy.
Szkolenie Python od podstaw, przez analizę danych, do machine learning
To szkolenie może być dofinansowane dla Ciebie z KFS lub BUR.
★★★★★Średnia ocena naszych szkoleń w Google: 5/5
Komentarze (0)
Brak komentarzy...