Jaka jest różnica między iteratorami a generatorami? Przydałoby się kilka przykładów użycia każdego przypadku.
iterator
jest bardziej ogólną koncepcją: każdy obiekt, którego klasa ma next
metodę ( __next__
w Pythonie 3) i __iter__
metodę, która ją posiada return self
.
Każdy generator jest iteratorem, ale nie odwrotnie. Generator jest zbudowany przez wywołanie funkcji, która ma jedno lub więcej yield
wyrażeń ( yield
instrukcji w Pythonie 2.5 i wcześniejszych) i jest obiektem spełniającym definicję an z poprzedniego akapitu iterator
.
Możesz użyć niestandardowego iteratora zamiast generatora, gdy potrzebujesz klasy o nieco złożonym zachowaniu utrzymującym stan lub chcesz ujawnić inne metody oprócz next
( __iter__
i __init__
). Najczęściej generator (czasami, dla wystarczająco prostych potrzeb, wyrażenie generatora ) jest wystarczający i jest łatwiejszy do kodowania, ponieważ utrzymanie stanu (w rozsądnych granicach) jest w zasadzie „wykonywane dla ciebie” przez zawieszenie i wznowienie ramki.
Na przykład generator, taki jak:
def squares(start, stop):
for i in range(start, stop):
yield i * i
generator = squares(a, b)
lub równoważne wyrażenie generatora (genexp)
generator = (i*i for i in range(a, b))
zajęłoby więcej kodu do zbudowania jako niestandardowy iterator:
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def next(self): # __next__ in Python 3
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
iterator = Squares(a, b)
Ale oczywiście z klasą Squares
można łatwo zaoferować dodatkowe metody, tj
def current(self):
return self.start
jeśli rzeczywiście potrzebujesz takiej dodatkowej funkcjonalności w swojej aplikacji.
for ... in ...:
, albo funkcja, albo zadzwonisziter.next()
for..in
składni. Może czegoś mi brakowało, ale to było jakiś czas temu, nie pamiętam, czy rozwiązałem. Dziękuję Ci!Podsumowując: Iteratory to obiekty, które mają metodę
__iter__
i__next__
(next
w Pythonie 2). Generatory zapewniają łatwy, wbudowany sposób tworzenia instancji Iteratorów.Funkcja z wydajnością jest nadal funkcją, która po wywołaniu zwraca instancję obiektu generatora:
Wyrażenie generatora zwraca również generator:
Aby uzyskać bardziej szczegółową ekspozycję i przykłady, czytaj dalej.
Generator to iterator
W szczególności generator jest podtypem iteratora.
Generator możemy stworzyć na kilka sposobów. Bardzo powszechnym i prostym sposobem na to jest funkcja.
W szczególności funkcja z wydajnością jest funkcją, która po wywołaniu zwraca generator:
Generator znowu jest iteratorem:
Iterator to Iterable
Iterator to Iterable,
co wymaga
__iter__
metody zwracającej Iterator:Niektóre przykłady iterable to wbudowane krotki, listy, słowniki, zestawy, zestawy zamrożone, ciągi, ciągi bajtów, tablice bajtów, zakresy i widoki pamięci:
Iterators wymagają
next
lub__next__
metodyW Python 2:
A w Python 3:
Możemy pobrać iteratory z wbudowanych obiektów (lub obiektów niestandardowych) za pomocą
iter
funkcji:__iter__
Metoda jest wywoływana, gdy spróbujesz użyć obiektu w pętli for. Następnie__next__
metoda jest wywoływana na obiekcie iteratora w celu pobrania każdego elementu do pętli. Iterator podnosi się,StopIteration
gdy go wyczerpałeś i nie można go ponownie użyć w tym momencie.Z dokumentacji
Z sekcji Typy generatorów w sekcji Typy iteratorów w dokumentacji Typów wbudowanych :
(Podkreślenie dodane.)
Z tego dowiadujemy się, że Generatory to (wygodny) typ Iteratora.
Przykładowe obiekty iteratora
Możesz utworzyć obiekt, który implementuje protokół Iterator, tworząc lub rozszerzając własny obiekt.
Ale łatwiej jest po prostu użyć Generatora, aby to zrobić:
Lub może prościej, Wyrażenie Generatora (działa podobnie do wyrażeń listowych):
Wszystkie mogą być używane w ten sam sposób:
Wniosek
Możesz użyć protokołu Iterator bezpośrednio, gdy potrzebujesz rozszerzyć obiekt Python jako obiekt, który można iterować.
Jednak w zdecydowanej większości przypadków najlepiej jest użyć
yield
do zdefiniowania funkcji, która zwraca Iterator Generatora lub rozważenia Wyrażeń Generatora.Na koniec zauważ, że generatory zapewniają jeszcze większą funkcjonalność jako coroutines. Wyjaśniam Generatory wraz ze
yield
stwierdzeniem dogłębnie w mojej odpowiedzi na „Co robi słowo kluczowe„ wydajność ”?”.źródło
Iteratory:
Iterator to obiekty, które wykorzystują
next()
metodę do uzyskania następnej wartości sekwencji.Generatory:
Generator to funkcja, która generuje lub generuje sekwencję wartości przy użyciu
yield
metody.Każde
next()
wywołanie metody na obiekcie generatora (np.f
Jak w poniższym przykładzie) zwrócone przez funkcję generatora (na przykład:foo()
funkcja w poniższym przykładzie), generuje kolejną wartość w sekwencji.Wywołanie funkcji generatora powoduje zwrócenie obiektu generatora nawet bez rozpoczęcia wykonywania funkcji. Gdy
next()
metoda jest wywoływana po raz pierwszy, funkcja rozpoczyna wykonywanie, dopóki nie osiągnie instrukcji dochodu, która zwraca uzyskaną wartość. Wydajność śledzi np. Pamięta ostatnie wykonanie. Drugienext()
połączenie jest kontynuowane od poprzedniej wartości.Poniższy przykład pokazuje wzajemne oddziaływanie między wydajnością a wywołaniem kolejnej metody na obiekcie generatora.
źródło
Dodanie odpowiedzi, ponieważ żadna z istniejących odpowiedzi nie odnosi się konkretnie do zamieszania w oficjalnej literaturze.
Funkcje generatora to zwykłe funkcje zdefiniowane za pomocą
yield
zamiastreturn
. Po wywołaniu funkcja generatora zwraca obiekt generatora , który jest rodzajem iteratora - manext()
metodę. Po wywołaniunext()
zwracana jest następna wartość uzyskana przez funkcję generatora.Zarówno funkcja, jak i obiekt mogą być nazywane „generatorem” w zależności od tego, który dokument źródłowy w Pythonie czytasz. Python słowniczek mówi funkcji generatora, podczas gdy Python wiki implikuje obiekty generatora. Samouczek Python wybitnie zarządza się sugerować zarówno widma w przestrzeni trzech zdaniach:
Pierwsze dwa zdania identyfikują generatory z funkcjami generatora, a trzecie zdanie identyfikuje je z obiektami generatora.
Pomimo całego tego zamieszania, można znaleźć odniesienie do języka Python dla jasnego i końcowego słowa:
Tak więc, w formalnym i precyzyjnym użyciu, „generator” bez zastrzeżeń oznacza obiekt generatora, a nie funkcję generatora.
Powyższe odniesienia dotyczą Python 2, ale odniesienie do języka Python 3 mówi to samo. Jednak glosariusz Python 3 stwierdza, że
źródło
Każdy ma naprawdę miłą i pełną odpowiedzi odpowiedź z przykładami i bardzo to doceniam. Chciałem tylko udzielić krótkiej linijki odpowiedzi dla osób, które wciąż nie są całkiem jasne koncepcyjnie:
Jeśli utworzysz własny iterator, jest to trochę zaangażowane - musisz stworzyć klasę i przynajmniej zaimplementować iter i kolejne metody. Ale co, jeśli nie chcesz przejść przez ten problem i chcesz szybko utworzyć iterator. Na szczęście Python zapewnia skrót do definiowania iteratora. Wszystko, co musisz zrobić, to zdefiniować funkcję z co najmniej 1 wywołaniem, aby uzyskać, a teraz, gdy wywołasz tę funkcję, zwróci „ coś ”, co będzie działać jak iterator (możesz wywołać następną metodę i użyć jej w pętli for). To coś ma w Pythonie nazwę Generator
Mam nadzieję, że to trochę wyjaśnia.
źródło
Poprzednie odpowiedzi pomijały ten dodatek: generator ma
close
metodę, podczas gdy typowe iteratory nie. Taclose
metoda wyzwalaStopIteration
wyjątek w generatorze, który może zostać przechwycony wfinally
klauzuli w tym iteratorze, aby uzyskać szansę na przeprowadzenie czyszczenia. Ta abstrakcja sprawia, że jest najbardziej użyteczna w dużych niż prostych iteratorach. Generator można zamknąć tak, jak można zamknąć plik, bez zawracania sobie głowy tym, co jest pod spodem.To powiedziawszy, moja osobista odpowiedź na pierwsze pytanie brzmiałaby: iterowalna ma
__iter__
tylko metodę, typowe iteratory mają__next__
tylko metodę, generatory mają zarówno__iter__
i, jak__next__
i dodatkowąclose
.W przypadku drugiego pytania moja osobista odpowiedź brzmiałaby: w publicznym interfejsie mam tendencję do faworyzowania generatorów, ponieważ jest on bardziej odporny:
close
metoda ma większą kompozycjęyield from
. Lokalnie mogę używać iteratorów, ale tylko wtedy, gdy jest to płaska i prosta struktura (iteratory nie dają się łatwo komponować) i jeśli istnieją powody, by sądzić, że sekwencja jest raczej krótka, szczególnie jeśli można ją zatrzymać przed osiągnięciem końca. Zwykle patrzę na iteratory jako na prymitywny poziom niski, z wyjątkiem literałów.W kwestiach związanych z przepływem sterowania generatory są równie ważną koncepcją, jak obietnice: oba są abstrakcyjne i można je komponować.
źródło
__iter__
metodę, to dlaczego iterator może mieć__next__
tylko? Gdyby miały to być iterowalne, oczekiwałbym, że również będą musiały__iter__
.__iter__
iterabeli do zwrócenia iteratora, który wymaga tylkonext
metody (__next__
w Python3). Proszę nie mylić standardów (dotyczących pisania kaczego) z ich implementacją (jak zaimplementował to konkretny interpreter Pythona). To trochę przypomina pomieszanie funkcji generatora (definicja) i obiektów generatora (implementacja). ;)Funkcja Generatora jest jak zwykła funkcja w Pythonie, ale zawiera jedną lub więcej
yield
instrukcji. Funkcje generatora to świetne narzędzie do tworzenia obiektów Iterator tak łatwo, jak to możliwe. Iterator obiekt returend przez funkcję generatora jest również nazywany obiektem Generator lub generatora .W tym przykładzie utworzyłem funkcję Generator, która zwraca obiekt Generator
<generator object fib at 0x01342480>
. Podobnie jak inne iteratory, obiekty Generatora mogą być używane wfor
pętli lub z wbudowaną funkcją,next()
która zwraca następną wartość z generatora.Zatem funkcja generatora jest najprostszym sposobem na utworzenie obiektu Iterator.
Każdy obiekt generatora jest iteratorem, ale nie odwrotnie. Niestandardowy obiekt iteratora można utworzyć, jeśli jego klasa implementuje metodę
__iter__
i__next__
metodę (zwaną także protokołem iteratora).Jednak o wiele łatwiej jest używać funkcji generatorów do tworzenia iteratorów, ponieważ upraszczają one ich tworzenie, ale niestandardowy Iterator daje większą swobodę i możesz także implementować inne metody zgodnie z własnymi wymaganiami, jak pokazano w poniższym przykładzie.
źródło
Przykłady z Ned Batchelder wysoce zalecane dla iteratorów i generatorów
Metoda bez generatorów, które robią coś z liczbami parzystymi
podczas korzystania z generatora
return
oświadczeniaWywołanie
evens
metody (generator) jest jak zwykleIterator
i ta zakładka nie ma nic innego, jak się przenieść
next
Aby użyć Generatora ... potrzebujemy funkcji
Aby użyć Iteratora ... potrzebujemy
next
iiter
Jak powiedziano:
Cała zaleta Iteratora:
źródło
Możesz porównać oba podejścia dla tych samych danych:
Poza tym, jeśli sprawdzisz powierzchnię pamięci, generator zajmie znacznie mniej pamięci, ponieważ nie musi jednocześnie przechowywać wszystkich wartości w pamięci.
źródło
Piszę specjalnie dla początkujących w Pythonie w bardzo prosty sposób, choć w głębi duszy Python robi tak wiele rzeczy.
Zacznijmy od bardzo podstawowego:
Rozważ listę,
Napiszmy równoważną funkcję:
o / p z
print(l): [1,2,3]
& o / p zprint(f()) : [1,2,3]
Zróbmy listę l iterowalną: Na liście python jest zawsze iterowalna, co oznacza, że możesz zastosować iterator, kiedy chcesz.
Zastosujmy iterator na liście:
Stwórzmy iterowalną funkcję, tj. Napisz równoważną funkcję generatora. W python, jak tylko wprowadzisz słowo kluczowe
yield
; staje się funkcją generatora i iterator zostanie zastosowany niejawnie.Uwaga: Każdy generator jest zawsze iterowalny z zastosowanym niejawnym iteratorem, a tutaj ukryty iterator jest najważniejszy. Więc funkcja generatora będzie:
Więc jeśli zauważyłeś, jak tylko utworzyłeś funkcję generatora fa, jest już iteracyjny (f)
Teraz,
Trochę rzucasz int na int (x), który już jest int i pozostanie int (x).
Na przykład o / p:
jest
Nigdy nie zapominaj, że jest to Python, a nie C lub C ++
Stąd wniosek z powyższego wyjaśnienia jest następujący:
źródło