Yield w Python pozwala tworzyć funkcje generujące dane w sposób sekwencyjny i pamięciooszczędny — generatory umożliwiają iterację wielkich zbiorów bez ładowania ich do pamięci naraz.
Czym jest yield?
Yield to słowo kluczowe w Pythonie, które umożliwia funkcji zwracanie wartości bez całkowitego kończenia jej działania. Gdy funkcja napotyka yield, wykonanie jest wstrzymywane, a jej stan (wartości lokalnych zmiennych, pozycja w kodzie) zostaje zapamiętany. Przy kolejnym wywołaniu (np. przez next()), funkcja kontynuuje pracę od punktu, gdzie została zatrzymana, aż do kolejnego yield lub końca działania.
Różnica – yield vs return
| Return | Yield |
|---|---|
| Kończy funkcję i zwraca pojedynczą wartość | Wstrzymuje funkcję i zwraca wartość |
| Funkcja uruchamia się tylko raz | Funkcja działa tyle razy, ile razy po nią sięgniemy |
| Zwraca konkretny obiekt | Zwraca generator (obiekt iterowalny) |
Funkcja z yield „pamięta” swój stan między wywołaniami, umożliwiając generowanie elementów na żądanie. To jak używanie zakładki w książce — kontynuujesz czytanie od miejsca, gdzie przerwałeś.
Co to jest generator?
Generator to obiekt powstały z funkcji zawierającej yield. Można po nim iterować – jak po liście – ale kolejne elementy są tworzone „w locie”, tylko gdy ich potrzebujesz, bez trzymania wszystkich naraz w pamięci.
Przykład generowania liczb
def liczby_do_5():
for i in range(1, 6):
yield i
gen = liczby_do_5()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
Każde wywołanie next(gen) uruchamia funkcję do kolejnego yield i zwraca wartość.
Mechanika działania – pauzowanie i zachowywanie stanu
Kiedy generator osiągnie yield:
- wykonywanie funkcji zostaje zatrzymane,
- zwracana jest aktualna wartość,
- stan funkcji zostaje zapamiętany (wszystkie lokalne zmienne i pozycja w kodzie),
- następne wywołanie
next()wznawia działanie tuż po yield.
To pozwala działać na ogromnych zbiorach danych (np. plikach, dużych kolekcjach) bez zużywania dużej ilości RAMu.
Typowe zastosowania
Generatorów i yield warto używać, gdy:
- przetwarzasz duże pliki lub strumienie danych (np. tysiące, miliony rekordów),
- nie chcesz trzymać wszystkiego w pamięci,
- tworzysz leniwe (lazy) przepływy danych, np. w strumieniach, pipeline’ach,
- piszesz kod do obsługi, np. czytania linii pliku, generowania sekwencji, obliczeń na żywo.
Przykład – leniwe czytanie linii z pliku
def czytaj_linie_z_pliku(nazwa_pliku):
with open(nazwa_pliku) as f:
for linia in f:
yield linia.strip()
for linia in czytaj_linie_z_pliku("logi.txt"):
print(linia)
Zaawansowane aspekty generatorów
Obsługa wyjątków
Jeśli generator „skończy się” (brak kolejnego yield), wywołanie next() podnosi wyjątek StopIteration. Zazwyczaj generatorów używa się w pętlach for, które automatycznie obsługują ten wyjątek.
Współpraca z innymi narzędziami
Generator można:
- przekazywać do funkcji,
- łączyć z innymi generatorami (np. za pomocą yield from),
- używać jako element pipeline przetwarzania danych.
Generator expressions (wyrażenia generatorowe)
Oprócz funkcji z yield, można tworzyć generatory „w locie” przy użyciu wyrażeń generatorowych:
gen = (x*x for x in range(10))
for n in gen:
print(n)
Działa jak list comprehensions, ale nie tworzy od razu całej listy.
Wydajność i efektywność
Generatory pozwalają pisać pamięciooszczędny i leniwy kod. Wydajne są szczególnie przy:
- procesowaniu dużych zbiorów,
- łańcuchowaniu wielu operacji iteracyjnych (np. filtrowanie, mapowanie).
Podsumowanie – kiedy wybrać yield?
Stosuj yield oraz generatory gdy:
- liczy się pamięciooszczędność,
- chcesz strumieniowo przetwarzać dane,
- implementujesz własne iteratory,
- wymagasz elastycznego i skalowalnego kodu.
Yield i generatory to jedno z najpotężniejszych narzędzi w Pythonie do pisania efektywnych algorytmów, skalujących się do dużych zbiorów i umożliwiających programowanie funkcyjno-strumieniowe.
Przykładowe źródła do pogłębienia tematu (YouTube, blogi, kursy online).