Skirtumas tarp „Python“ generatorių ir iteratorių

Koks skirtumas tarp iteratorių ir generatorių? Kai kurie pavyzdžiai, kada naudosite kiekvieną atvejį, bus naudingi.

311
06 мая '10 в 0:14 2010-05-06 00:14 newToProgramavimas yra nustatytas gegužės 06 d. 10 val. 0:14 2010-05-06 00:14
@ 7 atsakymai

iterator yra bendresnė sąvoka: bet koks objektas, kurio klasė turi kitą metodą ( __next__ Python 3) ir metodas __iter__ , kuris return self .

Kiekvienas generatorius yra iteratorius, bet ne atvirkščiai. Generatorius sukuriamas paskambinus funkcijai, turinčiai vieną ar daugiau yield ( yield ) išraiškų „Python 2.5“ ir ankstesnėse), ir yra objektas, atitinkantis ankstesnę iterator apibrėžimo dalį.

Galbūt norėsite naudoti pasirinktinį iteratorių, o ne generatorių, kai jums reikia klasės su sudėtingu būklės elgesiu arba norėdami atskleisti kitus metodus, išskyrus next (ir __iter__ ir __init__ ). Dažniausiai generatorius (kartais gana paprastiems poreikiams) ) yra pakankamas ir lengviau koduoti, nes viešosios paslaugos (pagrįstomis ribomis) iš esmės yra „padarytos jums“, kai rėmas tampa sustabdomas ir atnaujinamas.

Pavyzdžiui, generatorius, pavyzdžiui:

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

arba generatoriaus ekvivalentinė išraiška (genexp)

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

reikia daugiau kodo, kad galėtumėte sukurti kaip pasirinktinį iteratorių:

 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) 

Bet, žinoma, naudojant „ Squares klasę, galite lengvai pasiūlyti papildomų metodų, t.

  def current(self): return self.start 

jei jums reikia realių tokių papildomų funkcijų jūsų paraiškoje.

347
06 мая '10 в 0:19 2010-05-06 00:19 atsakė Alex Martelli gegužės 06 d. 10 val. 0:19 2010-05-06 00:19

Koks skirtumas tarp iteratorių ir generatorių? Kai kurie pavyzdžiai, kada naudosite kiekvieną atvejį, bus naudingi.

Trumpai tariant: Iteratoriai yra objektai, turintys __iter__ ir __next__ ( next Python 2). Generatoriai pateikia paprastą, įmontuotą būdą, kaip sukurti iteratoriaus atvejus.

Funkcija su išėjimu vis dar yra funkcija, kuri, kai ji vadinama, grąžina generatoriaus objekto egzempliorių:

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

Generatoriaus išraiška taip pat grąžina generatorių:

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

Išsamesniam pristatymui ir pavyzdžiams tęsti skaitymą.

Generatorius yra iteratorius

Visų pirma, generatorius yra iteratoriaus potipis.

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

Generatorius gali būti sukurtas keliais būdais. Labai paprastas ir paprastas būdas tai padaryti su funkcija.

Ypač funkcija, turinti išvestį, yra funkcija, kuri, kai ji vadinama, grąžina generatorių:

 >>> 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'> 

Ir generatorius vėl yra iteratorius:

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

Iteratorius yra pasikartojantis

Pakartotinis iteratorius,

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

kuris reikalauja __iter__ metodo, kurį Iterator grąžina:

 >>> 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__ 

Kai kurie iteracijų pavyzdžiai yra eilutės, sąrašai, rinkiniai, skersai, eilutės ir diapazono objektai:

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

Iteratoriai reikalauja next arba __next__

„Python 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 

Python 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__ 

Mes galime gauti iteratorių iš įterptųjų objektų (arba pasirinktinių objektų) naudojant iter funkciją:

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

Funkcija __iter__ yra tai, kas vadinama bandant naudoti objektą su kilpa. Tada __next__ arba next yra vadinamas iteratoriaus objektu, kad gautumėte kiekvieną kilpos elementą. Iterator, kai jūs jį išnaudojote, padidina „ StopIteration , ir šiuo metu jo negalima pakartotinai naudoti.

Iš dokumentų:

Integruotų dokumentų tipų skyriuje „Iteratorių tipai“ esančioje skiltyje „Generatorių tipai“:

Pythonų generatoriai yra patogus būdas įgyvendinti iteratoriaus protokolą. Jei __iter__() konteinerio objektas yra įgyvendinamas kaip generatorius, jis automatiškai grąžins iteratoriaus objektą (techniškai generatoriaus objektą), pateikdamas __iter__() ir next() [ __next__() Python 3]. Daugiau informacijos apie generatorius galima rasti išeigos išraiška dokumentuose.

(Pabrėžta pridėta.)

Taigi iš to mes sužinosime, kad Generatoriai yra (patogus) tipo Iteratorius.

Iteratorių objektų pavyzdžiai

Galite sukurti objektą, įgyvendinantį „Iterator“ protokolą, kurdami arba išplėsti savo objektą.

 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 

Tačiau lengviau tiesiog naudoti generatorių:

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

Arba, galbūt paprastesnė, išraiška Generatorius (veikia kaip sąrašai):

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

Jie visi gali būti naudojami taip pat:

 >>> 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 

Išvada

„Iterator“ protokolą galite naudoti tiesiogiai, kai reikia išplėsti „Python“ objektą kaip objektą, kurį galima pakartoti.

Tačiau daugeliu atvejų geriausia naudoti yield kad apibrėžtų funkciją, kuri grąžina generatorių arba atsižvelgia į generatoriaus išraiškas.

Galiausiai, atkreipkite dėmesį, kad generatoriai suteikia dar daugiau funkcionalumo korutinų pavidalu. Kartu su yield nurodymu generatoriui paaiškinu, kad mano atsakymas į klausimą „Ką reiškia raktinis žodis“?

43
05 февр. atsakymą pateikė Aaron Hall 05 Feb. 2015-02-05 23:12 '15, 23:12 2015-02-05 23:12

iteratoriai:

Iteratoriai yra objektai, kurie naudoja next() metodą, kad gautų kitą sekos vertę.

Generatoriai:

Generatorius yra funkcija, kuria gaunama arba gaunama reikšmių seka, naudojant yield metodą.

Kiekvienas next() metodas next() vadina generatoriaus objektą (pvz., f , kaip parodyta žemiau), grąžinamas generatoriaus funkcijos (pvz., foo() žemiau pateiktame pavyzdyje), generuoja sekančią vertę sekoje.

Kai vadinama generatoriaus funkcija, ji grąžina generatoriaus objektą, net nepradėdama atlikti funkcijos. Kai next() metodas vadinamas pirmą kartą, funkcija pradeda vykdyti, kol pasiekia išeigos ataskaitą, kuri grąžina gautą vertę. Išvesties takeliai, ty, prisimena paskutinį atlikimą. Ir antrasis kvietimas į next() tęsiasi nuo ankstesnės vertės.

Toliau pateiktame pavyzdyje parodyta išvesties sąveika ir kito generatoriaus objekto metodo skambinimas.

 >>> 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 atsakymą pateikė vartotojo966588 gegužės 19, 14 d., 10:05 val. 2014-05-19 22:05

Įtraukus atsakymą, nes nė vienas iš esamų atsakymų nėra susijęs su painiava oficialioje literatūroje.

Generatoriaus funkcijos yra normalios funkcijos, apibrėžtos naudojant yield o ne return . Kai funkcija vadinama, generatorius grąžina objekto generatorių , kuris yra tam tikras iteratorius - jis turi next() metodą. Kai skambinate next() , grąžinama kita generatoriaus funkcijos grąžinta vertė.

Funkcija arba objektas gali būti vadinamas „generatoriumi“, priklausomai nuo to, kurį Python šaltinio dokumentą skaitote. „Python“ žodynėlis kalba apie generatoriaus funkcijas, o „ Python wiki“ reiškia generatoriaus objektus. „Python“ pamoka puikiai sugeba naudoti abu naudojimo būdus trimis sakiniais:

Generatoriai yra paprastas ir galingas įrankis kuriant iteratorius. Jie yra parašyti kaip įprastos funkcijos, tačiau jie naudoja grąžos operatorių, kai nori grąžinti duomenis. Kiekvieną kartą kitą kartą () jį iškviesti, generatorius vėl pradeda veikti (jis prisimena visas duomenų reikšmes ir paskutinį pareiškimą).

Pirmieji du sakiniai apibrėžia generatorius su generatoriaus funkcijomis, o trečias sakinys identifikuoja juos su generatoriaus objektais.

Nepaisant šio painiavos, galite rasti nuorodą į „Python“ kalbą, kad būtų aiškus ir aiškus žodis:

Išeigos išraiška naudojama tik apibrėžiant generatoriaus funkciją ir gali būti naudojama tik funkcijų apibrėžties kūno dalyje. Naudojant išeigos išraišką funkcijų apibrėžime, pakanka priversti šią apibrėžtį sukurti generatoriaus funkciją, o ne įprastą funkciją.

Kai vadinama generatoriaus funkcija, ji grąžina iteratorių, žinomą kaip generatorius. Tada šis generatorius valdo generatoriaus funkciją.

Taigi, formaliai ir tiksliai naudojant „generatorių“, nekvalifikuota reiškia generatoriaus objektą, o ne generatoriaus funkciją.

Pirmiau pateiktos nuorodos nurodo Python 2, bet Python 3 kalbos nuoroda sako tą patį. Tačiau „ Python 3“ žodynėlis nurodo

generatorius ... Paprastai nurodoma generatoriaus funkcija, tačiau kai kuriuose kontekstuose jis gali būti susijęs su generatoriaus iteratoriumi. Tais atvejais, kai numatoma reikšmė nėra aiški, visiško termino vartojimas vengia dviprasmiškumo.

12
08 марта '16 в 3:05 2016-03-08 03:05 atsakymą pateikė Paulius kovo 8 d. 16 d. 3:05 2016-03-08 03:05

Generatoriaus funkcija, generatoriaus objektas, generatorius

Generatoriaus funkcija yra panaši į įprastą funkciją „Python“, tačiau turi vieną ar daugiau yield . Generatoriaus funkcijos yra puiki priemonė, kaip sukurti kuo lengviau Iterator objektus. Generatoriaus funkcija, kurią grąžina generatorius, taip pat vadinamas generatoriumi arba generatoriumi .

Šiame pavyzdyje sukūriau „Generator“ funkciją, kuri grąžina generatoriaus objekto <generator object fib at 0x01342480> . Kaip ir kiti iteratoriai, generatoriaus objektai gali būti naudojami kilpoje arba su integruota next() funkcija, kuri grąžina kitą generatoriaus vertę.

 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 

Taigi generatoriaus funkcija yra lengviausias būdas sukurti „Iterator“ objektą.

Iteratorius

Kiekvienas generatorius yra iteratorius , bet ne atvirkščiai. Pasirinktinį iteratoriaus objektą galima sukurti, jei jo klasė įgyvendina __iter__ ir __next__ (dar vadinamus iteratoriaus protokolu).

Tačiau daug lengviau naudoti generatoriaus funkciją, kad sukurtumėte iteratorius , nes jie supaprastina jų kūrimą, tačiau pasirinktinis „Iterator“ suteikia jums daugiau laisvės, taip pat galite įgyvendinti kitus jūsų reikalavimus atitinkančius metodus, kaip parodyta toliau pateiktame pavyzdyje.

 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 окт. Atsakymas, kurį pateikė Navtaj Randhawa spalio 27 d 2016-10-27 01:21 '16 at 1:21 am 2016-10-27 01:21

Kiekvienas žmogus turi tikrai gražų ir išsamų atsakymą su pavyzdžiais, ir aš tai labai vertinu. Aš tik norėjau trumpai atsakyti į kelias eilutes žmonėms, kurie vis dar nėra visiškai aiškūs:

Jei kuriate savo iteratorių, tai šiek tiek susiję - turite sukurti klasę ir bent jau įdiegti iter ir šiuos metodus. Bet kas, jei nenorite įveikti šios problemos ir norite greitai sukurti iteratorių. Laimei, „Python“ suteikia nuorodą į iteratoriaus apibrėžimą. Viskas, ką jums reikia padaryti, yra apibrėžti funkciją su bent vienu skambučiu, o dabar, kai skambinate šią funkciją, ji grąžins „ kažką “, kuris veiks kaip iteratorius (galite skambinti šiuo metodu ir naudoti jį „for loop“ ). Tai kažkas turi Python pavadinimą, vadinamą Generatoriumi

Tikiuosi, kad tai šiek tiek paaiškins.

2
09 сент. atsakymas į „ Heapify“ rugsėjo 09 d 2017-09-09 04:14 '17 at 4:14 2017-09-09 04:14

Galite palyginti tuos pačius duomenis abiem būdais:

 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))) 

Be to, jei patikrinate atminties dydį, generatorius sunaudoja daug mažiau atminties, nes nereikia tuo pačiu metu išsaugoti visų atminties reikšmių.

1
16 марта '17 в 18:45 2017-03-16 18:45 Atsakymas pateikiamas tashuhka kovo 16 d. 17 val. 18:45 2017-03-16 18:45

Kiti klausimai apie žymes arba Užduoti klausimą