Ką daro našumo raktinis žodis?

Koks yra „Python“ yield raktinio žodžio naudojimas? Ką tai daro?

Pavyzdžiui, bandau suprasti šį kodą 1 :

 def _get_child_candidates(self, distance, min_dist, max_dist): if self._leftchild and distance - max_dist < self._median: yield self._leftchild if self._rightchild and distance + max_dist >= self._median: yield self._rightchild 

Ir tai yra rinkiklis

 result, candidates = [], [self] while candidates: node = candidates.pop() distance = node._get_dist(obj) if distance <= max_dist and distance >= min_dist: result.extend(node._values) candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) return result 

Kas atsitinka, kai _get_child_candidates metodas? Ar sąrašas grąžinamas? Vienas elementas? Ar tai dar kartą vadinama? Kada bus baigti tolesni skambučiai?


1. Kodas paimtas iš Jochen Schulz (jrschulz), kuris sukūrė puikią Python biblioteką metrinėms erdvėms. Tai yra nuoroda į visą šaltinį: „Mspace“ modulis .

8911
Alexas. 24 окт. S. 24 okt. 2008-10-24 01:21 '08 1:21 am. 2008-10-24 01:21
@ 46 atsakymai
  • 1
  • 2

Norėdami suprasti, kas yra yield , reikia suprasti, kas yra generatoriai. Ir prieš generatoriai ateina į iteratorius.

iteruotas

Kai sukuriate sąrašą, galite jį perskaityti po vieną. Jo elementų skaitymas po vieną vadinamas iteracija:

 >>> mylist = [1, 2, 3] >>> for i in mylist: ... print(i) 1 2 3 

mylist yra pakartojamas. Kai naudojatės sąrašo supratimu, sukuriate sąrašą ir todėl kartojate:

 >>> mylist = [x*x for x in range(3)] >>> for i in mylist: ... print(i) 0 1 4 

Viskas, ką galite naudoti " for... in... " yra iteracinis; lists , strings , failai ...

Šios iteracijos yra patogu, nes galite skaityti tiek, kiek norite, bet jūs išsaugosite visas atmintyje esančias vertybes, ir tai ne visada tai, ko norite, kai turite daug vertybių.

Generatoriai

Generatoriai yra iteratoriai, tam tikras kartojimas, kurį galite kartoti tik vieną kartą . Generatoriai nesaugo visų vertybių atmintyje, jie generuoja vertes skrydžio metu :

 >>> mygenerator = (x*x for x in range(3)) >>> for i in mygenerator: ... print(i) 0 1 4 

Tai yra tas pats, išskyrus tai, kad naudojote () vietoj [] . Bet antrą kartą negalėsite daryti mano generatoriuje, nes generatoriai gali būti naudojami tik vieną kartą: jie apskaičiuoja 0, tada pamiršti apie jį ir apskaičiuoja 1, o galiausiai apskaičiuoja 4, vienas po kito.

Išeiga

yield yra raktinis žodis, kuris naudojamas kaip return , išskyrus tai, kad funkcija grąžins generatorių.

 >>> def createGenerator(): ... mylist = range(3) ... for i in mylist: ... yield i*i ... >>> mygenerator = createGenerator() # create a generator >>> print(mygenerator) # mygenerator is an object! <generator object createGenerator at 0xb7555c34> >>> for i in mygenerator: ... print(i) 0 1 4 

Čia yra nenaudingas pavyzdys, tačiau naudinga, kai žinote, kad jūsų funkcija grąžins didžiulę vertybių rinkinį, kurį jums reikia perskaityti tik vieną kartą.

Norėdami susidoroti su yield , jūs turite suprasti, kad kai skambinate funkcijai, funkcijos, įrašytos į kūno dalį, neprasideda. Funkcija grąžina tik generatoriaus objektą, tai yra šiek tiek sudėtinga :-)

Tada jūsų kodas bus tęsiamas nuo tada, kai jis išjungė kiekvieną kartą, for naudoti generatorių.

Dabar sudėtingiausia dalis:

Kai pirmą kartą skambinate for iškviečiamas generatorius, sukurtas iš jūsų funkcijos, jis veiks jūsų funkcijoje nuo pat pradžių, kol pasieks yield , ir tada grąžina pirmąją kilpos vertę. Tada kiekvienas paskesnis skambutis pradės kilpą, kurią vėl parašėte funkcijai, ir grąžina kitą vertę, kol bus grąžinta vertė.

Generatorius yra laikomas tuščiu po to, kai funkcija pradeda veikti, bet nebėra yield . Tai gali būti dėl to, kad ciklas baigėsi arba dėl to, kad nebeatitinkate "if/else" .


Jūsų kodas paaiškintas

Generatorius:

 # Here you create the method of the node object that will return the generator def _get_child_candidates(self, distance, min_dist, max_dist): # Here is the code that will be called each time you use the generator object: # If there is still a child of the node object on its left # AND if distance is ok, return the next child if self._leftchild and distance - max_dist < self._median: yield self._leftchild # If there is still a child of the node object on its right # AND if distance is ok, return the next child if self._rightchild and distance + max_dist >= self._median: yield self._rightchild # If the function arrives here, the generator will be considered empty # there is no more than two values: the left and the right children 

Abonentas:

 # Create an empty list and a list with the current object reference result, candidates = list(), [self] # Loop on candidates (they contain only one element at the beginning) while candidates: # Get the last candidate and remove it from the list node = candidates.pop() # Get the distance between obj and the candidate distance = node._get_dist(obj) # If distance is ok, then you can fill the result if distance <= max_dist and distance >= min_dist: result.extend(node._values) # Add the children of the candidate in the candidates list # so the loop will keep running until it will have looked # at all the children of the children of the children, etc. of the candidate candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) return result 

Šiame kode yra keletas protingų dalių:

  • Ciklas kartojamas sąraše, tačiau sąrašas plečiasi ciklo iteracijos metu :-) Tai trumpas būdas pereiti visus šiuos įdėtus duomenis, net jei tai yra šiek tiek pavojinga, nes galite gauti begalinę kilpą. Tokiu atveju candidates.extend(node._get_child_candidates(distance, min_dist, max_dist)) išnaudoja visas generatoriaus reikšmes, tačiau while toliau kurdami naujus generatoriaus objektus, kurie generuos kitas nei ankstesnes reikšmes, nes jis netaikomas tam pačiam mazgų .

  • Metodas extend() yra sąrašo objekto metodas, kuris laukia iteracijos ir prideda jo reikšmes į sąrašą.

Paprastai mes pateikiame jam sąrašą:

 >>> a = [1, 2] >>> b = [3, 4] >>> a.extend(b) >>> print(a) [1, 2, 3, 4] 

Bet jūsų kode jis gauna generatorių, kuris yra geras, nes:

  1. Nereikia nuskaityti reikšmių du kartus.
  2. Jums gali būti daug vaikų, ir jūs nenorite, kad visi būtų laikomi atmintyje.

Ir tai veikia, nes „Python“ nerūpi, ar metodo argumentas yra sąrašas, ar ne. „Python“ laukia iteracijos, todėl jis veiks su stygomis, sąrašais, rinkiniais ir generatoriais! Tai vadinama ančiuku ir yra viena iš priežasčių, kodėl Python yra toks kietas. Bet tai dar viena istorija kitam klausimui ...

Čia galite sustoti arba truputį skaityti, kad pamatytumėte, ar generatorius naudoja papildomai:

Generatoriaus išsekimo kontrolė

 >>> class Bank(): # Let create a bank, building ATMs ... crisis = False ... def create_atm(self): ... while not self.crisis: ... yield "$100" >>> hsbc = Bank() # When everything ok the ATM gives you as much as you want >>> corner_street_atm = hsbc.create_atm() >>> print(corner_street_atm.next()) $100 >>> print(corner_street_atm.next()) $100 >>> print([corner_street_atm.next() for cash in range(5)]) ['$100', '$100', '$100', '$100', '$100'] >>> hsbc.crisis = True # Crisis is coming, no more money! >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> wall_street_atm = hsbc.create_atm() # It even true for new ATMs >>> print(wall_street_atm.next()) <type 'exceptions.StopIteration'> >>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty >>> print(corner_street_atm.next()) <type 'exceptions.StopIteration'> >>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business >>> for cash in brand_new_atm: ... print cash $100 $100 $100 $100 $100 $100 $100 $100 $100 ... 

Pastaba „Python 3“ naudokite print(corner_street_atm.__next__()) arba print(next(corner_street_atm))

Tai gali būti naudinga įvairiems dalykams, pvz., Prieigos prie išteklių kontrolei.

Itertools, tavo geriausias draugas

„Itertools“ modulyje yra specialios iteracijų valdymo funkcijos. Ar kada nors norėjote kartoti generatorių? Dviejų generatorių grandinė? Grupuokite vertybes įdėtame sąraše su viena eilute? Map/Zip nesukuriant kito sąrašo?

Tada tiesiog import itertools .

Pavyzdys? Pažvelkime į galimas arklių lenktynių atvykimo procedūras:

 >>> horses = [1, 2, 3, 4] >>> races = itertools.permutations(horses) >>> print(races) <itertools.permutations object at 0xb754f1dc> >>> print(list(itertools.permutations(horses))) [(1, 2, 3, 4), (1, 2, 4, 3), (1, 3, 2, 4), (1, 3, 4, 2), (1, 4, 2, 3), (1, 4, 3, 2), (2, 1, 3, 4), (2, 1, 4, 3), (2, 3, 1, 4), (2, 3, 4, 1), (2, 4, 1, 3), (2, 4, 3, 1), (3, 1, 2, 4), (3, 1, 4, 2), (3, 2, 1, 4), (3, 2, 4, 1), (3, 4, 1, 2), (3, 4, 2, 1), (4, 1, 2, 3), (4, 1, 3, 2), (4, 2, 1, 3), (4, 2, 3, 1), (4, 3, 1, 2), (4, 3, 2, 1)] 

Suprasti vidaus iteracijos mechanizmus

Iteracija yra procesas, kuris reiškia iteracijas (taikant __iter__() metodą) ir iteratorius (įgyvendinant __next__() metodą. Iteracijos yra bet kokie objektai, iš kurių galite gauti iteratorių. Iteratoriai yra objektai, kurie leidžia pakartoti iteracijas.

Šiame straipsnyje yra daugiau informacijos apie tai, kaip dirbti .

13022
24 окт. Atsakymą pateikė e-satis 24 oct. 2008-10-24 01:48 '08 ne 1:48 2008-10-24 01:48

Etiketė prie graižymo yield

Kai matote funkciją su yield , naudokite šį paprastą triuką, kad suprastumėte, kas atsitiks:

  1. Įrašykite result = [] funkcijos pradžioje.
  2. Pakeiskite kiekvieną yield expr iš rezultato.append result.append(expr) .
  3. Įdėkite return result eilutės return result funkcijos apačioje.
  4. Taip - ne daugiau yield ! Perskaitykite ir sužinokite kodą.
  5. Palyginkite funkciją su pradiniu apibrėžimu.

Šis metodas gali suteikti jums idėjos apie funkcijos logiką, bet tai, kas iš tikrųjų atsitinka su yield , gerokai skiriasi nuo to, kas vyksta remiantis sąrašu. Daugeliu atvejų pelningumo metodas bus daug efektyvesnis ir greitesnis. Kitais atvejais šis triukas bus įstrigęs begalinėje kilpoje, net jei pradinė funkcija veikia gerai. Skaitykite toliau ir sužinokite daugiau ...

Negalima painioti savo iteratorių, iteratorių ir generatorių.

Pirma, iteratoriaus protokolas - kai rašote

 for x in mylist: ...loop body... 

„Python“ atlieka šiuos du veiksmus:

  1. mylist iteratorių mylist :

    Skambinant iter(mylist) → grąžinamas objektas su next() metodu (arba __next__() Python 3).

    [Tai yra žingsnis, kurį dauguma žmonių pamiršo kalbėti]

  2. Naudoja iteratorių prie kilpos elementų:

    Toliau skambinkite next() metodu iteratoriuje, kuris grąžinamas iš 1 žingsnio. Grįžtamoji vertė next() priskirta x ir kilpos kūnas. Jei „ StopIteration išimtis vadinama iš vidaus next() , tai reiškia, kad iteratoriuje ir ciklo pabaigoje nėra daugiau reikšmių.

Tiesa ta, kad „Python“ atlieka pirmiau minėtus du veiksmus bet kuriuo metu, kai nori kartoti per objekto turinį - todėl jis gali būti už kilpą, bet jis taip pat gali būti kodas, pvz., otherlist.extend(mylist) (kai otherlist sąrašas yra „Python“ sąrašas ),

border=0

Čia mylist yra iteracinis, nes jis įgyvendina iteratoriaus protokolą. Naudotojo apibrėžtoje klasėje galite įgyvendinti __iter__() metodą, kad jūsų klasės egzemplioriai būtų kartojami. Šis metodas turėtų grąžinti iteratorių. Iteratorius yra next() metodas. Jūs galite įgyvendinti tiek __iter__() tiek next() tą pačią klasę ir turėti __iter__() grįžtamąjį self . Tai veiks paprastais atvejais, bet ne tada, kai norite, kad du iteratoriai tuo pačiu metu ciklo tą patį objektą.

Taigi iteratoriaus protokole daugelis objektų įgyvendina šį protokolą:

  1. Įdiegti sąrašai, žodynai, rinkiniai, rinkiniai, failai.
  2. Individualios klasės, kurios įdiegia __iter__() .
  3. Generatoriai.

Atkreipkite dėmesį, kad „ for loop“ nežino, su kokiu objektu jis susiduria - jis tiesiog seka iteratoriaus protokolą ir mielai kviečia gauti elementą po skambučio next() . Įdiegti sąrašai grąžina elementus po vieną, žodynai grįžta klavišus po vieną. Ir generatoriai sugrįžta ... gerai, kai yield :

 def f123(): yield 1 yield 2 yield 3 for item in f123(): print item 

Vietoj yield , jei f123() buvo trys return f123() operatoriai f123() tik pirmoji ir f123() funkcija. Tačiau f123() nėra įprasta funkcija. Kai f123() , jis nepateikia jokios vertės pelningumo ataskaitose! Grąžina generatoriaus objektą. Be to, funkcija iš tikrųjų neatsiranda - ji patenka į laukimo būseną. Kai „ for loop“ bando sugeneruoti generatoriaus objektą, funkcija iš savo pristabdytos būsenos grįžta iš kitos eilutės po to, kai anksčiau grąžintas rezultatų yield , atlieka kitą eilutės eilutę, šiuo atveju yield , ir grąžina jį kaip kitą elementą. Taip atsitinka, kol funkcija bus paleista ir šiuo metu yra StopIteration generatorius ir StopIteration ciklas.

Taigi generatoriaus objektas yra panašus į adapterį - viename gale jis parodo iteratoriaus protokolą, suteikiantį __iter__() ir next() kad palaikytų gerą kilpą. Tačiau kitame gale ji pradeda funkciją, kuri yra pakankama, kad gautų kitą vertę, ir vėl ją įjungia į laukimo režimą.

Kodėl naudoti generatorius?

Paprastai galite rašyti kodą, kuris nenaudoja generatorių, bet įgyvendina tą pačią logiką. Vienas variantas yra naudoti anksčiau minėtą laikiną triukų sąrašą. Tai visais atvejais neveiks, pavyzdžiui, jei turite begalines kilpas, arba jis gali sukelti neefektyvų atminties naudojimą, kai turite tikrai ilgą sąrašą. Kitas metodas yra įgyvendinti naują iteracinę klasę „ SomethingIter kuri išsaugo būseną egzemplioriaus elementuose ir atlieka kitą loginį žingsnį next() metodu (arba __next__() Python 3). Priklausomai nuo logikos, next() metodo kodas gali atrodyti labai sudėtingas ir linkęs į klaidas. Čia generatoriai teikia švarų ir paprastą sprendimą.

1744 m
26 окт. vartotojo 28409 atsakymas 2008-10-26 00:22 '08 0:22 2008-10-26 00:22

Pagalvokite apie tai:

Iteratorius yra tik išgalvotas terminas objektui, turinčiam kitą () metodą. Taigi, išvesties funkcija galiausiai atrodo taip:

Originali versija:

 def some_function(): for i in xrange(4): yield i for i in some_function(): print i 

Tai iš esmės yra tai, ką „Python“ vertėjas atlieka su aukščiau pateiktu kodu:

 class it: def __init__(self): # Start at -1 so that we get 0 when we add 1 below. self.count = -1 # The __iter__ method will be called once by the 'for' loop. # The rest of the magic happens on the object returned by this method. # In this case it is the object itself. def __iter__(self): return self # The next method will be called repeatedly by the 'for' loop # until it raises StopIteration. def next(self): self.count += 1 if self.count < 4: return self.count else: # A StopIteration exception is raised # to signal that the iterator is done. # This is caught implicitly by the 'for' loop. raise StopIteration def some_func(): return it() for i in some_func(): print i 

Norėdami geriau suprasti, kas vyksta užkulisiuose, „ for loop“ galima perrašyti taip:

 iterator = some_func() try: while 1: print iterator.next() except StopIteration: pass 

Ar tai yra prasmingesnė ar tiesiog painioja jus? :)

Turiu pabrėžti, kad tai supaprastinimas, skirtas iliustracijai. :)

441
24 окт. Atsakymas Jasonui Bakeriui spalio 24 d 2008-10-24 01:28 '08 at 1:28 2008-10-24 01:28

yield raktinis žodis yra dviejų paprastų faktų:

  1. Jei kompiliatorius aptinka yield raktinį žodį bet kurioje funkcijos dalyje, ši funkcija nebėra grąžinta per return . Vietoj to jis nedelsdamas grąžina tingų laukimo sąrašo objektą, vadinamą generatoriumi.
  2. Generatorius kartojamas. Kas yra kartojama? Tai yra kažkas panašaus į list set arba diktuojamą vaizdą su įterptiniu protokolu, kad būtų galima aplankyti kiekvieną elementą tam tikra tvarka.

Trumpai tariant: generatorius yra tingus, palaipsniui didėjantis sąrašas , o yield leidžia naudoti žymėjimo funkciją, kad užprogramuotų sąrašo reikšmes, kurias generatorius turėtų palaipsniui išleisti.

 generator = myYieldingFunction(...) x = list(generator) generator v [x[0], ..., ???] generator v [x[0], x[1], ..., ???] generator v [x[0], x[1], x[2], ..., ???] StopIteration exception [x[0], x[1], x[2]] done list==[x[0], x[1], x[2]] 

pavyzdys

Nustatykime funkciją makeRange kuri yra panaši į Python range . „ makeRange(n) skambutis makeRange(n) generatorių:

 def makeRange(n): # return 0,1,2,...,n-1 i = 0 while i < n: yield i i += 1 >>> makeRange(5) <generator object makeRange at 0x19e4aa0> 

Jei norite, kad generatorius nedelsiant grąžintų laukiančias vertes, galite ją perkelti į list() (kaip ir bet kurį kartotinį):

 >>> list(makeRange(5)) [0, 1, 2, 3, 4] 

Lyginant pavyzdį su „tiesiog grąžinti sąrašą“

Anksčiau pateiktas pavyzdys gali būti laikomas tiesiog sukuriančiu sąrašą, į kurį įtraukiate ir grąžinate:

 # list-version # # generator-version def makeRange(n): # def makeRange(n): """return [0,1,2,...,n-1]""" #~ """return 0,1,2,...,n-1""" TO_RETURN = [] #> i = 0 # i = 0 while i < n: # while i < n: TO_RETURN += [i] #~ yield i i += 1 # i += 1 ## indented return TO_RETURN #> >>> makeRange(5) [0, 1, 2, 3, 4] 

Tačiau yra vienas didelis skirtumas; Žr. Paskutinį skyrių.


Kaip galite naudoti generatorius

Iteruotas yra paskutinė sąrašo supratimo dalis, ir visi generatoriai yra kartotiniai, todėl jie dažnai naudojami taip:

 # _ITERABLE_ >>> [x+10 for x in makeRange(5)] [10, 11, 12, 13, 14] 

Norint geriau suprasti generatorius, galite žaisti su „ itertools moduliu (būtinai naudokite chain.from_iterable O ne chain su chain garantija. Pavyzdžiui, netgi galite naudoti generatorius, kad galėtumėte įgyvendinti be galo ilgus tingus sąrašus, pvz., itertools.count() . Jūs galite įgyvendinti savo pačių def enumerate(iterable): zip(count(), iterable) arba, tai padaryti, naudodamiesi yield raktiniu žodžiu.

Atkreipkite dėmesį, kad generatoriai gali būti naudojami daugeliu kitų tikslų, pvz., Coroutines, ne deterministinis programavimas ar kiti elegantiški dalykai. Tačiau „tingus sąrašai“ vaizdas, kurį čia atstovauju, yra labiausiai paplitusi naudojimo sritis, kurią rasite.


Užkulisiuose

Taip veikia Python Iteration Protocol. Taip atsitinka, kai list(makeRange(5)) . Tai, ką anksčiau apibūdinau kaip „tingus, papildomas sąrašas“.

 >>> x=iter(range(5)) >>> next(x) 0 >>> next(x) 1 >>> next(x) 2 >>> next(x) 3 >>> next(x) 4 >>> next(x) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration 

next() integruota funkcija tiesiog vadina .next() objektus .next() , kuris yra „iteracijos protokolo“ dalis, ir vyksta visuose iteratoriuose. Galite rankiniu būdu naudoti next() funkciją (ir kitas iteracijos protokolo dalis) neįprastiems dalykams, paprastai skaitymo sąskaita, įgyvendinti, todėl pabandykite to nedaryti ...


mažai dalykų

Paprastai dauguma žmonių nesirūpina šiais skirtumais ir tikriausiai norės čia nustoti skaityti.

Python'e iteratyvas yra bet koks objektas, kuris „supranta kilpos sąvoką“, pavyzdžiui, sąrašas [1,2,3] , o iteratorius yra specifinė prašomos kilpos, pavyzdžiui, [1,2,3].__iter__() . Generatorius yra lygiai toks pat kaip bet koks iteratorius, išskyrus tai, kaip jis buvo parašytas (su funkcijos sintaksė).

Kai iš sąrašo prašote iteratoriaus, jis sukuria naują iteratorių. Tačiau, kai jūs prašote iteratoriaus iš iteratoriaus (kurį retai darote), jis paprasčiausiai suteikia jums kopiją.

Taigi, mažai tikėtina, kad negalėsite kažko panašaus padaryti ...

 > x = myRange(5) > list(x) [0, 1, 2, 3, 4] > list(x) [] 

... tada prisiminkite, kad generatorius yra iteratorius; t. y. vienkartinis naudojimas. Jei norite ją pakartotinai naudoti, turėtumėte „ myRange(...) skambinti „ myRange(...) . Jei jums reikia du kartus naudoti rezultatą, konvertuokite rezultatą į sąrašą ir išsaugokite jį kintamajame x = list(myRange(5)) . Tie, kurie būtinai turi klonuoti generatorių (pvz., itertools.tee siaubingai įsilaužėlių metaprogramavimą), gali naudoti itertools.tee jei tai būtina, nes Python PEP standarto pasiūlymas iteratoriui buvo atidėtas.

378
19 июня '11 в 9:33 2011-06-19 09:33 atsakymas pateikiamas ninjagecko birželio 19 d. 11 val. 9:33 2011-06-19 09:33

Ką daro yield raktinis žodis „python“?

Atsakymo schema / santrauka

  • Skambučio grąžos funkcija grąžina generatorių .
  • Generatoriai yra iteratoriai, nes jie įgyvendina iteratoriaus protokolą , todėl galite juos kartoti.
  • Informacija taip pat gali būti siunčiama generatoriui, todėl ji konceptualiai yra coroutine .
  • „Python 3“ galite perkelti iš vieno generatoriaus į kitą abiem kryptimis naudodami yield from .
  • (Приложение критикует пару @, включая верхний, и обсуждает использование return в генераторе.)

Генераторы:

yield допустим только внутри определения функции, и включение yield в определение функции заставляет его возвращать генератор.

Идея для генераторов исходит из других языков (см. Сноску 1) с различными реализациями. В Python Generators выполнение кода заморожено в точке выхода. Когда вызывается генератор (методы обсуждаются ниже), выполнение возобновляется, а затем останавливается при следующем выходе.

yield предоставляет простой способ реализации протокола итератора , который определяется следующими двумя методами: __iter__ и next (Python 2) или __next__ (Python 3). Оба эти метода делают объект итератором, который можно проверить типом с помощью абстрактного базового класса Iterator из модуля collections .

 >>> def func(): ... yield 'I am' ... yield 'a generator!' ... >>> type(func) # A function with yield is still a function <type 'function'> >>> gen = func() >>> type(gen) # but it returns a generator <type 'generator'> >>> hasattr(gen, '__iter__') # that an iterable True >>> hasattr(gen, 'next') # and with .next (.__next__ in Python 3) True # implements the iterator protocol.