Razlika između Python generatora i iteratora

Koja je razlika između iteratora i generatora? Neki primjeri kada ćete koristiti svaki slučaj bit će od pomoći.

311
06 мая '10 в 0:14 2010-05-06 00:14 novoToProgramiranje je postavljeno Svibanj 06 '10 u 0:14 2010-05-06 00:14
@ 7 odgovora

iterator je općenitiji pojam: bilo koji objekt čija klasa ima next metodu ( __next__ u Pythonu 3) i metodu __iter__ koja čini return self .

Svaki generator je iterator, ali ne i obrnuto. Generator se kreira pozivanjem funkcije koja ima jedan ili više izraza u yield ( yield ) u Pythonu 2.5 i ranije) i objekt je koji odgovara prethodnom odlomku definicije iterator .

Vi svibanj želite koristiti prilagođeni iterator, a ne generator, kada vam je potrebna klasa s pomalo složenim stanjem ponašanja, ili želite izložiti druge metode osim next (i __iter__ i __init__ ). ) je dovoljno, i lakše je kodirati, jer su javne usluge (u razumnim granicama) u osnovi "učinjene za vas" kada okvir postane suspendiran i nastavlja se.

Na primjer, generator kao što je:

 def squares(start, stop): for i in range(start, stop): yield i * i generator = squares(a, b) 

ili ekvivalentni izraz generatora (genexp)

 generator = (i*i for i in range(a, b)) 

potreban je dodatni kôd za izradu prilagođenog iteratora:

 class Squares(object): def __init__(self, start, stop): self.start = start self.stop = stop def __iter__(self): return self def next(self): if self.start >= self.stop: raise StopIteration current = self.start * self.start self.start += 1 return current iterator = Squares(a, b) 

Ali, naravno, s klasom Squares , možete jednostavno predložiti dodatne metode, tj.

  def current(self): return self.start 

ako imate stvarne potrebe za takvim dodatnim značajkama u svojoj prijavi.

347
06 мая '10 в 0:19 2010-05-06 00:19 odgovorio je Alex Martelli 06. svibnja 2010. u 0:19 2010-05-06 00:19

Koja je razlika između iteratora i generatora? Neki primjeri kada ćete koristiti svaki slučaj bit će od pomoći.

Ukratko: Iteratori su objekti koji imaju __iter__ i __next__ ( next u Pythonu 2). Generatori pružaju jednostavan, ugrađeni način stvaranja instanci iteratora.

Funkcija s izlazom u njoj je još uvijek funkcija koja, kada se pozove, vraća instancu objekta generatora:

 def a_function(): "when called, returns generator object" yield 

Izraz generatora također vraća generator:

 a_generator = (i for i in range(0)) 

Za detaljniju prezentaciju i primjere, nastavite čitati.

Generator je iterator

Konkretno, generator je podtip iteratora.

 >>> import collections, types >>> issubclass(types.GeneratorType, collections.Iterator) True 

Možemo stvoriti generator na nekoliko načina. Vrlo jednostavan i jednostavan način da to učinite s funkcijom.

Konkretno, funkcija s izlazom u njoj je funkcija koja, kada se pozove, vraća generator:

 >>> def a_function(): "just a function definition with yield in it" yield >>> type(a_function) <class 'function'> >>> a_generator = a_function() # when called >>> type(a_generator) # returns a generator <class 'generator'> 

I generator je opet iterator:

 >>> isinstance(a_generator, collections.Iterator) True 

Iterator je iterabilan

Iterator je iterabilan,

 >>> issubclass(collections.Iterator, collections.Iterable) True 

koja zahtijeva metodu __iter__ , koju Iterator vraća:

 >>> collections.Iterable() Traceback (most recent call last): File "<pyshell#79>", line 1, in <module> collections.Iterable() TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__ 

Neki primjeri iteracija su tuples, liste, skupovi, diktati, nizovi i objekti raspona:

 >>> all(isinstance(element, collections.Iterable) for element in ( (), [], {}, set(), '', range(0))) True 

Iteratori zahtijevaju next ili __next__

U Pythonu 2:

border=0
 >>> collections.Iterator() Traceback (most recent call last): File "<pyshell#80>", line 1, in <module> collections.Iterator() TypeError: Can't instantiate abstract class Iterator with abstract methods next 

I u Pythonu 3:

 >>> collections.Iterator() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class Iterator with abstract methods __next__ 

Možemo dobiti iteratore iz ugrađenih objekata (ili prilagođenih objekata) pomoću funkcije iter :

 >>> all(isinstance(iter(element), collections.Iterator) for element in ( (), [], {}, set(), '', range(0))) True 

Funkcija __iter__ je ono što se zove kada pokušavate koristiti objekt s petljom for. Tada se __next__ ili next naziva na objektu iteratora da bi dobio svaki element za petlju. Iterator podiže StopIteration kada ste ga iscrpili i ne može se ponovno upotrijebiti u ovom trenutku.

Iz dokumenata:

U odjeljku "Vrste generatora" odjeljka "Vrste Iteratora" ugrađenih vrsta dokumentacije :

Python generatori pružaju prikladan način za implementaciju iteratorskog protokola. Ako je objekt spremnika __iter__() implementiran kao generator, automatski će vratiti objekt iteratora (tehnički, objekt generatora) davanjem __iter__() i next() [ __next__() u Pythonu 3]. Više informacija o generatorima možete pronaći u dokumentaciji za izražavanje prinosa.

(Naglasak dodan.)

Tako smo saznali da su Generatori (prikladan) tip Iteratora.

Primjeri objekata iteratora

Možete stvoriti objekt koji implementira Iterator protokol stvaranjem ili proširenjem vlastitog objekta.

 class Yes(collections.Iterator): def __init__(self, stop): self.x = 0 self.stop = stop def __iter__(self): return self def next(self): if self.x < self.stop: self.x += 1 return 'yes' else: # Iterators must raise when done, else considered broken raise StopIteration __next__ = next # Python 3 compatibility 

Ali za to je jednostavnije koristiti generator:

 def yes(stop): for _ in range(stop): yield 'yes' 

Ili, možda jednostavniji, izraz Generator (radi kao popisi):

 yes_expr = ('yes' for _ in range(stop)) 

Svi se mogu koristiti na isti način:

 >>> stop = 4 >>> for i, ys in enumerate(zip(Yes(stop), yes(stop), ('yes' for _ in range(stop))): >>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop), ('yes' for _ in range(stop))): ... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3)) ... 0: yes == yes == yes 1: yes == yes == yes 2: yes == yes == yes 3: yes == yes == yes 

zaključak

Možete koristiti protokol Iterator izravno kada trebate proširiti Python objekt kao objekt koji se može ponoviti.

Međutim, u većini slučajeva najbolje je koristiti yield za definiranje funkcije koja vraća generator ili razmatra izraze generatora.

Konačno, imajte na umu da generatori pružaju još više funkcionalnosti u obliku koroutina. Ja objasniti generator zajedno s yield upute na dubinu mog odgovora na pitanje "Što ključna riječ" prinos "učiniti?

43
05 февр. odgovor daje Aaron Hall 05 Feb. 2015-02-05 23:12 '15 u 23:12 2015-02-05 23:12

iteratora:

Iteratori su objekti koji koriste next() metodu za dobivanje sljedeće vrijednosti niza.

generatori:

Generator je funkcija koja proizvodi ili proizvodi niz vrijednosti koristeći metodu yield .

Svaka metoda next() poziva objekt generatora (za ex: f , kao što je prikazano u nastavku), koji se vraća funkcijom generatora (na primjer: foo() u primjeru ispod), generira sljedeću vrijednost u nizu.

Kada se pozove funkcija generatora, ona vraća objekt generatora, čak i bez pokretanja funkcije. Kada se next() metoda pozove po prvi put, funkcija počinje izvršavanje dok ne dosegne izraz prinosa, koji vraća dobivenu vrijednost. Izlazni zapisi, tj. Pamte posljednju izvedbu. Drugi poziv na next() nastavlja se od prethodne vrijednosti.

Sljedeći primjer prikazuje interakciju između izlaza i pozivanja sljedeće metode za objekt generatora.

 >>> def foo(): ... print "begin" ... for i in range(3): ... print "before yield", i ... yield i ... print "after yield", i ... print "end" ... >>> f = foo() >>> f.next() begin before yield 0 # Control is in for loop 0 >>> f.next() after yield 0 before yield 1 # Continue for loop 1 >>> f.next() after yield 1 before yield 2 2 >>> f.next() after yield 2 end Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> 
24
19 мая '14 в 22:05 2014-05-19 22:05 odgovor je dao korisnik966588 19. svibnja '14 u 22:05 sati 2014-05-19 22:05

Dodavanje odgovora, jer niti jedan od postojećih odgovora ne spominje konfuziju u službenoj literaturi.

Generatorske funkcije su normalne funkcije definirane pomoću yield umjesto return . Kada se pozove funkcija, generator vraća generator objekta, koji je vrsta iteratora - ima next() metodu. Kada nazovete next() , vraća se sljedeća vrijednost koju vraća funkcija generatora.

Funkciju ili objekt možemo nazvati “generator”, ovisno o tome koji Python izvorni dokument čitate. Python glosar govori o generatorskim funkcijama, a Python wiki podrazumijeva objekte generatora. Python udžbenik iznimno uspijeva koristiti obje uporabe u tri rečenice:

Generatori su jednostavan i moćan alat za stvaranje iteratora. Oni se pišu kao normalne funkcije, ali koriste operatore prinosa kada žele vratiti podatke. Svaki put kada se pozove sljedeći (), generator nastavlja sa svojom akcijom (pamti sve vrijednosti podataka i zadnju izjavu).

Prve dvije rečenice definiraju generatore s generatorskim funkcijama, a treća rečenica ih identificira s generatorskim objektima.

Unatoč toj konfuziji, možete pronaći vezu na Python jezik za jasnu i konačnu riječ:

Izraz prinosa koristi se samo pri definiranju funkcije generatora i može se koristiti samo u tijelu definicije funkcije. Upotreba izraza prinosa u definiciji funkcije dovoljna je da ovu definiciju prisili stvoriti funkciju generatora umjesto normalne funkcije.

Kada se pozove funkcija generatora, vraća iterator, poznat kao generator. Ovaj generator zatim kontrolira funkciju generatora.

Stoga, uz formalnu i preciznu uporabu "generatora", nekvalificirani znači objekt generatora, a ne generatorsku funkciju.

Gore navedene veze odnose se na Python 2, ali Python 3 jezična referenca kaže isto. Međutim, Python 3 glosar navodi to

generator ... Obično se odnosi na funkciju generatora, ali se može odnositi na iterator generatora u nekim kontekstima. U slučajevima kada namjerno značenje nije jasno, upotreba punih pojmova izbjegava dvosmislenost.

12
08 марта '16 в 3:05 2016-03-08 03:05 odgovor je dao Paul ožujak 08 '16 u 3:05 2016-03-08 03:05

Funkcija generatora, objekt generatora, generator

Funkcija generatora je slična običnoj funkciji u Pythonu, ali sadrži jedan ili više yield . Generatorske funkcije su izvrstan alat za stvaranje Iterator objekata što je lakše moguće. Objekt Iterator koji se vraća pomoću funkcije generatora također se naziva objekt generatora ili generator .

U ovom primjeru, stvorio sam Generator funkciju, koja vraća Generator objekt <generator object fib at 0x01342480> . Kao i drugi iteratori, Generatorski objekti mogu se koristiti u petlji for ili s ugrađenom next() funkcijom koja vraća sljedeću vrijednost iz generatora.

 def fib(max): a, b = 0, 1 for i in range(max): yield a a, b = b, a + b print(fib(10)) #<generator object fib at 0x01342480> for i in fib(10): print(i) # 0 1 1 2 3 5 8 13 21 34 print(next(myfib)) #0 print(next(myfib)) #1 print(next(myfib)) #1 print(next(myfib)) #2 

Stoga je funkcija generatora najlakši način stvaranja Iterator objekta.

iteratora

Svaki objekt generatora je iterator , ali ne i obrnuto. Prilagođeni objekt iteratora može se kreirati ako njegova klasa implementira __iter__ i __next__ (također nazvane iterator protokol).

Međutim, mnogo je lakše koristiti funkciju generatora za stvaranje iteratora , jer pojednostavljuju njihovo kreiranje, ali prilagođeni Iterator daje vam više slobode, a također možete implementirati i druge metode koje odgovaraju vašim zahtjevima, kao što je prikazano u primjeru u nastavku.

 class Fib: def __init__(self,max): self.current=0 self.next=1 self.max=max self.count=0 def __iter__(self): return self def __next__(self): if self.count>self.max: raise StopIteration else: self.current,self.next=self.next,(self.current+self.next) self.count+=1 return self.next-self.current def __str__(self): return "Generator object" itobj=Fib(4) print(itobj) #Generator object for i in Fib(4): print(i) #0 1 1 2 print(next(itobj)) #0 print(next(itobj)) #1 print(next(itobj)) #1 
3
27 окт. Odgovor dao Navtaj Randhawa 27. listopada 2016-10-27 01:21 '16 u 1:21 am 2016-10-27 01:21

Svatko ima doista lijep i detaljan odgovor s primjerima, i ja to stvarno cijenim. Samo sam htjela dati kratak odgovor na nekoliko redaka za osobe koje još uvijek nisu potpuno jasne:

Ako stvarate vlastiti iterator, ovo je malo povezano - morate stvoriti klasu i barem implementirati iter i sljedeće metode. Ali što ako ne želite prevladati ovaj problem i želite brzo stvoriti iterator. Srećom, Python nudi prečac za definiranje iteratora. Sve što trebate učiniti jest definirati funkciju s najmanje jednim pozivom, a sada kada pozovete tu funkciju, vratit će se " nešto " koje će djelovati kao iterator (možete pozvati sljedeću metodu i koristiti je u for loop ). Ovo nešto ima naziv u Pythonu nazvanom Generator

Nadam se da ovo malo razjašnjava.

2
09 сент. odgovor dati Heapify rujan 09 2017-09-09 04:14 '17 u 4:14 2017-09-09 04:14

Možete usporediti oba pristupa za iste podatke:

 def myGeneratorList(n): for i in range(n): yield i def myIterableList(n): ll = n*[None] for i in range(n): ll[i] = i return ll # Same values ll1 = myGeneratorList(10) ll2 = myIterableList(10) for i1, i2 in zip(ll1, ll2): print("{} {}".format(i1, i2)) # Generator can only be read once ll1 = myGeneratorList(10) ll2 = myIterableList(10) print("{} {}".format(len(list(ll1)), len(ll2))) print("{} {}".format(len(list(ll1)), len(ll2))) # Generator can be read several times if converted into iterable ll1 = list(myGeneratorList(10)) ll2 = myIterableList(10) print("{} {}".format(len(list(ll1)), len(ll2))) print("{} {}".format(len(list(ll1)), len(ll2))) 

Osim toga, ako provjerite veličinu memorije, generator troši mnogo manje memorije, jer ne mora istovremeno pohranjivati ​​sve vrijednosti u memoriju.

1
16 марта '17 в 18:45 2017-03-16 18:45 Odgovor je dan tashuhka 16. ožujka 2006. u 18:45 2017-03-16 18:45

Ostala pitanja o oznakama ili Postavi pitanje