Obliczenia „na żądanie” lub leniwe obliczenia stały się popularne wraz ze wzrostem ilości przetwarzanych danych. W Pythonie takie mechanizmy zostały zaimplementowane w bibliotece standardowej poprzez generatory i iteratory, a później — poprzez funkcję itertools i klasy zdolne do zwracania pojedynczego elementu na żądanie, unikając jednoczesnego przechowywania wszystkich danych w pamięci.
Zwykłe budowanie kolekcji wymaga załadowania całego wyniku do pamięci. Jeśli objętość jest duża — program może „zawiesić się” lub działać bardzo wolno. Ważne jest umiejętność przetwarzania strumieni danych — na przykład plików o wielkości wielu gigabajtów lub wyników zapytań do API.
Leniwe obliczenia pozwalają na pobieranie elementów w miarę potrzeby. W Pythonie jest to ułatwione dzięki wykorzystaniu generatorów, składni yield, wyrażeń-generatorów, funkcji map, filter, zip, a także modułowi itertools. Takie podejście opiera się na protokole iteratorów.
Przykład kodu:
def huge_sequence(): for i in range(1, 10**9): yield i * i for val in huge_sequence(): if val > 100: break print(val)
Kluczowe cechy:
Czy generatory w Pythonie zawsze oszczędzają pamięć?
Odpowiedź: Nie, tylko jeśli dane faktycznie nie wymagają przechowywania pośredniego pomiędzy krokami. Niektóre konstrukcje, na przykład list comprehensions, tworzą całą listę od razu, podczas gdy generatory — tylko na żądanie. Jeśli pośrednie wyniki są nadal potrzebne, oszczędność zostaje utracona.
Przykład:
squares = (x**2 for x in range(10**8)) # leniwe, oszczędne result = list(squares) # natychmiast zajmuje całą pamięć
Czy to prawda, że map i filter zawsze zwracają listy?
Nie, w Pythonie 3 map i filter zwracają nie listę, a iterator (leniwy generator), co oszczędza pamięć i pozwala na przetwarzanie danych „w locie”.
Czy można wielokrotnie iterować po generatorze?
Nie, generator „wygasa” po pełnym przejściu. Jeśli potrzebne jest ponowne przejście, warto stworzyć nowy generator lub użyć kolekcji kontenerowej, której zawartość można wielokrotnie przeszukiwać.
Programista próbuje przetworzyć duży plik logów, ładując go do pamięci jako listę wierszy.
Zalety:
Wady:
Użyty jest generator — odczyt pliku wierszami z przetwarzaniem każdego wiersza w miarę ich pozyskiwania.
Zalety:
Wady: