Sukurkite pagrindinį Python iteratorių

Kaip sukurti iteracinę funkciją (arba iteratoriaus objektą) pythone?

495
21 авг. Akdom nustatė 21 d. 2008-08-21 03:36 '08, 3:36, 2008-08-21 03:36
@ 9 atsakymai

„Iterator“ objektai pythone atitinka iteratoriaus protokolą, kuris iš esmės reiškia, kad jie pateikia du metodus: __iter__() ir next() . __iter__ grąžina iteratoriaus objektą ir netiesiogiai vadinamas kilpų pradžioje. next() metodas grąžina kitą vertę ir netiesiogiai vadinamas kiekvienu kilpos prieaugiu. next() sukelia „StopIteration“ išimtį, kai nebėra grąžinimo vertės, netiesiogiai užfiksuotos kilpų kontūrų, kad sustabdytų iteraciją.

Štai paprastas kontrastinis pavyzdys:

 class Counter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def next(self): # Python 3: def __next__(self) if self.current > self.high: raise StopIteration else: self.current += 1 return self.current - 1 for c in Counter(3, 8): print c 

Tai spausdins:

 3 4 5 6 7 8 

Lengviau rašyti naudojant generatorių, kaip aprašyta ankstesniame atsakyme:

 def counter(low, high): current = low while current <= high: yield current current += 1 for c in counter(3, 8): print c 

Spausdinta produkcija bus tokia pati. Po gaubtu generatoriaus objektas palaiko iteratoriaus protokolą ir daro kažką panašaus į klasės skaitiklį.

David Merz straipsnis, „ Iterators“ ir „Simple Generators“ yra gana geras įvadas.

563
23 авг. atsakymas pateikiamas ars 23 rug. 2008-08-23 19:57 '08 at 7:57 pm 2008-08-23 19:57

Yra keturi būdai, kaip sukurti iteracinę funkciją:

Pavyzdžiai:

 # generator def uc_gen(text): for char in text: yield char.upper() # generator expression def uc_genexp(text): return (char.upper() for char in text) # iterator protocol class uc_iter(): def __init__(self, text): self.text = text self.index = 0 def __iter__(self): return self def __next__(self): try: result = self.text[self.index].upper() except IndexError: raise StopIteration self.index += 1 return result # getitem method class uc_getitem(): def __init__(self, text): self.text = text def __getitem__(self, index): result = self.text[index].upper() return result 

Jei norite matyti visus keturis metodus veikiant:

 for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem: for ch in iterator('abcde'): print ch, print 

Rezultatas:

 ABCDE ABCDE ABCDE ABCDE 
border=0

Pastaba

Dviejų tipų generatorių ( uc_gen ir uc_genexp ) negalima reversed() ; paprastam iteratoriui ( uc_iter ) magijos metodas yra __reversed__ (kuris turėtų grąžinti naują iteratorių, kuris eina atgal); ir iteracija getitem ( uc_getitem ) turėtų turėti magišką metodą __len__ :

  # for uc_iter def __reversed__(self): return reversed(self.text) # for uc_getitem def __len__(self) return len(self.text) 

Kad atsakytumėte į nedidelį pulkininko Paniko klausimą apie begališkai vertinamą iteratorių, tai yra pavyzdžiai, naudojant kiekvieną iš keturių pirmiau išvardytų metodų:

 # generator def even_gen(): result = 0 while True: yield result result += 2 # generator expression def even_genexp(): return (num for num in even_gen()) # or even_iter or even_getitem # not much value under these circumstances # iterator protocol class even_iter(): def __init__(self): self.value = 0 def __iter__(self): return self def __next__(self): next_value = self.value self.value += 2 return next_value # getitem method class even_getitem(): def __getitem__(self, index): return index * 2 import random for iterator in even_gen, even_genexp, even_iter, even_getitem: limit = random.randint(15, 30) count = 0 for even in iterator(): print even, count += 1 if count >= limit: break print 

Rezultatas yra (bent jau mano mėginio rodymui):

 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 
362
25 сент. Atsakymą pateikė Ethan Furman 25 rugsėjis 2011-09-25 01:13 '11 at 13:13 2011-09-25 01:13

Visų pirma, „ itertools“ modulis yra neįtikėtinai naudingas visais atvejais, kai iteratorius yra naudingas, bet viskas, ko jums reikia norint sukurti iteratorių, yra:

Išeiga

Ar ne tai kietas? Išėjimas gali būti naudojamas norint pakeisti įprastą grąžinimo funkciją. Jis grąžina objektą tokiu pačiu būdu, bet vietoj sunaikinimo ir išėjimo iš valstybės, jis išsaugo būseną, kai norite atlikti kitą iteraciją. Čia pateikiamas pavyzdys, kaip elgtis tiesiogiai iš itertoolių funkcijų sąrašo :

 def count(n=0): while True: yield n n += 1 

Kaip nurodyta funkcijų aprašyme (tai yra skaičiavimo () funkcija iš itertoolių modulio ...), ji sukuria iteratorių, kuris grąžina iš eilės einančius sveikuosius skaičius, pradedant nuo n.

Generatoriaus išraiškos yra dar viena kirminų (nuostabių kirminų!) Gali. Jie gali būti naudojami vietoj „Supratimo“ sąrašo, kad būtų galima išsaugoti atmintį (atminties sąrašų sąrašai sunaikinami po naudojimo, jei nėra priskirtas kintamasis, tačiau generatoriaus išraiškos gali sukurti objekto generatorių ... kuris yra išgalvotas būdas pranešti iteratoriui). Čia pateikiamas generatoriaus išraiškos apibrėžimo pavyzdys:

 gen = (n for n in xrange(0,11)) 

Tai labai panašus į anksčiau pateiktą iteratoriaus apibrėžimą, išskyrus tai, kad yra pateiktas visas intervalas nuo 0 iki 10.

Aš ką tik sužinojau, kad () (nustebino, kad aš nemačiau to anksčiau ...) ir pridėjau pavyzdį. xrange () yra kartotinė diapazono () versija , kurios privalumas yra tai, kad jis nesukuria sąrašo iš anksto. Būtų labai naudinga, jei turėtumėte milžinišką duomenų kiekį, ir jūs turėjote tik tiek daug atminties, kad tai atliktumėte.

99
21 авг. atsakymas pateikiamas akdom 21 rug . 2008-08-21 03:36 '08, 3:36, 2008-08-21 03:36

Matau, kad kai kurie iš jūsų return self į __iter__ . Norėčiau atkreipti dėmesį, kad pats __iter__ gali būti generatorius (todėl nereikia __next__ ir didinti išimties StopIteration )

 class range: def __init__(self,a,b): self.a = a self.b = b def __iter__(self): i = self.a while i < self.b: yield i i+=1 

Žinoma, čia galite generuoti generatorių, tačiau sudėtingesnėms klasėms tai gali būti naudinga.

91
27 июля '12 в 18:05 2012-07-27 18:05 atsakymą pateikė „ Manux “ liepos 27 d. 12 val. 18:05 2012-07-27 18:05

Šis klausimas yra apie iteruotus objektus, o ne apie iteratorius. Pythone taip pat kartojamos sekos, todėl vienas būdas, kaip padaryti, kad iterable klasė būtų, kad ji elgtųsi kaip seka, t.y. Pateikite metodus __getitem__ ir __len__ . Aš išbandžiau jį 2 ir 3 Pythone.

 class CustomRange: def __init__(self, low, high): self.low = low self.high = high def __getitem__(self, item): if item >= len(self): raise IndexError("CustomRange index out of range") return self.low + item def __len__(self): return self.high - self.low cr = CustomRange(0, 10) for i in cr: print(i) 
9
21 марта '16 в 20:39 2016-03-21 20:39 atsakymas pateikiamas aq2 kovo 21 d., 16 val. 20:39 2016-03-21 20:39

Tai yra iteracinė funkcija be yield . Jis naudoja iter funkciją ir uždarymo funkciją, kuri saugo jį python 2 taikomojoje srityje kintamajame ( list ).

 def count(low, high): counter = [0] def tmp(): val = low + counter[0] if val < high: counter[0] += 1 return val return None return iter(tmp, None) 

„Python 3“ uždarymo būsena saugoma nepakeistoje taikymo srityje, o vietinėje zonoje naudojamas nonlocal , kad būtų galima atnaujinti būsenos kintamąjį.

 def count(low, high): counter = 0 def tmp(): nonlocal counter val = low + counter if val < high: counter += 1 return val return None return iter(tmp, None) 

Bandymas

 for i in count(1,10): print(i) 1 2 3 4 5 6 7 8 9 
3
03 марта '16 в 20:55 2016-03-03 20:55 Atsakymą pateikė Nizam Mohamed kovo 03 '16, 20:55 2016-03-03 20:55

Visi atsakymai šiame puslapyje yra tikrai geri sudėtingam objektui. Tačiau tiems, kuriuose yra integruotų iteracinių tipų kaip atributai, pvz., str , list , set arba dict , arba bet koks collections.Iterable įgyvendinimas. Šioje klasėje jūs galite praleisti tam tikrus dalykus savo klasėje.

 class Test(object): def __init__(self, string): self.string = string def __iter__(self): # since your string is already iterable return (ch for ch in string) 

Galima naudoti kaip:

 for x in Test("abcde"): print(x) # prints # a # b # c # d # e 
3
14 авг. Johno Stroodo atsakymas 14 rug. 2018-08-14 11:25 '18, 11:25

Jei ieškote kažko trumpo ir paprasto, galbūt jums to pakaks:

 class A(object): def __init__(self, l): self.data = l def __iter__(self): return iter(self.data) 

naudojimo pavyzdys:

 In [3]: a = A([2,3,4]) In [4]: [i for i in a] Out[4]: [2, 3, 4] 
2
26 апр. Daniil Mashkin atsakymas, pateiktas balandžio 26 d 2018-04-26 11:38 '18, 11:38

Įkvėptas Matt Gregory atsakymu, čia yra sudėtingesnis iteratorius, kuris grąžins a, b, ..., z, aa, ab, ..., zz, aaa, aab, ..., zzy, zzz

  class AlphaCounter: def __init__(self, low, high): self.current = low self.high = high def __iter__(self): return self def __next__(self): # Python 3: def __next__(self) alpha = ' abcdefghijklmnopqrstuvwxyz' n_current = sum([(alpha.find(self.current[x])* 26**(len(self.current)-x-1)) for x in range(len(self.current))]) n_high = sum([(alpha.find(self.high[x])* 26**(len(self.high)-x-1)) for x in range(len(self.high))]) if n_current > n_high: raise StopIteration else: increment = True ret = '' for x in self.current[::-1]: if 'z' == x: if increment: ret += 'a' else: ret += 'z' else: if increment: ret += alpha[alpha.find(x)+1] increment = False else: ret += x if increment: ret += 'a' tmp = self.current self.current = ret[::-1] return tmp for c in AlphaCounter('a', 'zzz'): print(c) 
0
13 июля '18 в 20:34 2018-07-13 20:34 atsakymą pateikė Ace.Di liepos 18 d. 18 val. 20:34 2018-07-13 20:34

Kiti klausimai apie žymes arba Užduoti klausimą