Ważną kwestią jest to, że analiza listy tworzy nową listę. Generator tworzy iterowalny obiekt, który „filtruje” materiał źródłowy „w locie”, gdy zużywasz bity.
Wyobraź sobie, że masz plik dziennika o wielkości 2 TB o nazwie „ogromny_plik.txt” i potrzebujesz zawartości i długości wszystkich wierszy rozpoczynających się od słowa „WEJŚCIE”.
Więc zacznij od napisania listy ze zrozumieniem:
logfile = open("hugefile.txt","r")
entry_lines = [(line,len(line)) for line in logfile if line.startswith("ENTRY")]
Spowalnia to cały plik, przetwarza każdą linię i przechowuje pasujące linie w tablicy. Ta tablica może zatem zawierać do 2 TB treści. To dużo pamięci RAM i prawdopodobnie nie jest praktyczne dla twoich celów.
Zamiast tego możemy użyć generatora, aby zastosować „filtr” do naszych treści. Żadne dane nie są odczytywane, dopóki nie zaczniemy iteracji nad wynikiem.
logfile = open("hugefile.txt","r")
entry_lines = ((line,len(line)) for line in logfile if line.startswith("ENTRY"))
Nawet żaden wiersz nie został jeszcze odczytany z naszego pliku. Powiedzmy, że chcemy jeszcze bardziej filtrować nasz wynik:
long_entries = ((line,length) for (line,length) in entry_lines if length > 80)
Wciąż nic nie zostało przeczytane, ale określiliśmy teraz dwa generatory, które będą działały na naszych danych, jak chcemy.
Wypiszmy nasze przefiltrowane linie do innego pliku:
outfile = open("filtered.txt","a")
for entry,length in long_entries:
outfile.write(entry)
Teraz czytamy plik wejściowy. Ponieważ nasza for
pętla nadal żąda dodatkowych linii, long_entries
generator żąda linii od entry_lines
generatora, zwracając tylko te, których długość jest większa niż 80 znaków. Z kolei entry_lines
generator żąda wierszy (filtrowanych jak wskazano) z logfile
iteratora, który z kolei odczytuje plik.
Dlatego zamiast „wypychać” dane do funkcji wyjściowej w postaci w pełni wypełnionej listy, dajesz funkcji wyjściowej sposób „wyciągania” danych tylko wtedy, gdy jest to potrzebne. W naszym przypadku jest to o wiele bardziej wydajne, ale nie tak elastyczne. Generatory są jednokierunkowe, jedno przejście; dane z odczytanego pliku dziennika są natychmiast odrzucane, więc nie możemy wrócić do poprzedniej linii. Z drugiej strony nie musimy martwić się o przechowywanie danych, gdy skończymy.
[exp for x in iter]
być po prostu cukierlist((exp for x in iter))
? czy jest różnica w wykonaniu?