Iš naujo nustatykite generatoriaus objektą „Python“

Turiu generatoriaus objektą, kurį grąžina keli išėjimai. Pasiruošimas skambinti šiam generatoriui yra gana sunkus darbas. Štai kodėl noriu keletą kartų naudoti generatorių.

 y = FunctionWithYield() for x in y: print(x) #here must be something to reset 'y' for x in y: print(x) 

Žinoma, atsižvelgiu į turinio kopijavimą į paprastą sąrašą.

92
13 авг. nustatė Dewfy 13 rug . 2009-08-13 14:10 '09, 14:10, 2009-08-13 14:10
@ 14 atsakymų

Kitas variantas yra naudoti itertools.tee() kad sukurtumėte antrą savo generatoriaus versiją:

 y = FunctionWithYield() y, y_backup = tee(y) for x in y: print(x) for x in y_backup: print(x) 

Tai gali būti naudinga atminties naudojimui, jei pirminis iteravimas negali apdoroti visų elementų.

69
13 авг. Ants Aasma atsakymas 13 rug . 2009-08-13 14:44 '09, 14:44 2009-08-13 14:44

Generatorių negalima perjungti. Turite šias parinktis:

  • Dar kartą paleiskite generatoriaus funkciją, iš naujo paleiskite kartą:

     y = FunctionWithYield() for x in y: print(x) y = FunctionWithYield() for x in y: print(x) 
  • Išsaugokite generatoriaus rezultatus duomenų struktūroje atmintyje arba diske, kurį galite pakartoti:

     y = list(FunctionWithYield()) for x in y: print(x) # can iterate again: for x in y: print(x) 
border=0

1 galimybės trūkumas yra tai, kad ji vėl apskaičiuoja vertes. Jei šis procesorius yra intensyvus, baigsite skaičiuoti du kartus. Kita vertus, trūkumas 2 yra saugojimas. Visas reikšmių sąrašas bus išsaugotas atmintyje. Jei per daug vertybių, tai gali būti nepraktiška.

Taigi, jūs turite klasikinį atminties ir apdorojimo derinį. Negaliu įsivaizduoti, kaip generatorius generuojamas atgal, neišsaugant vertybių ar jų skaičiuojant.

92
13 авг. Atsakymas pateikiamas nosklo 13 rug . 2009-08-13 14:18 '09, 14:18, 2009-08-13 14:18

Galbūt paprasčiausias sprendimas - įdėkite brangią dalį į objektą ir perkelti jį į generatorių:

 data = ExpensiveSetup() for x in FunctionWithYield(data): pass for x in FunctionWithYield(data): pass 

Taigi, galite talpinti brangius skaičiavimus.

Jei vienu metu galite išsaugoti visus rezultatus RAM, naudokite list() , kad generatorius gautų reguliarų sąrašą ir dirbtų su juo.

21
13 авг. atsakymas, kurį pateikė Aaron Digulla 2009-08-13 14:39 '09, 14:39, 2009-08-13 14:39
 >>> def gen(): ... def init(): ... return 0 ... i = init() ... while True: ... val = (yield i) ... if val=='restart': ... i = init() ... else: ... i += 1 >>> g = gen() >>> g.next() 0 >>> g.next() 1 >>> g.next() 2 >>> g.next() 3 >>> g.send('restart') 0 >>> g.next() 1 >>> g.next() 2 
20
16 окт. atsakymą pateikė „ aaab “ 16 d. 2013-10-16 15:21 '13, 15:21 2013-10-16 15:21

Noriu pasiūlyti dar vieną senos problemos sprendimą

 class ReusableGenerator: def __init__(self, generator_factory): self.generator_factory = generator_factory def __iter__(self): return self.generator_factory() squares = ReusableGenerator(lambda: (x * x for x in range(5))) for x in squares: print(x) for x in squares: print(x) 

Štai kaip jūs jį naudojate

 y = ReusableGenerator(function_with_yield) for x in y: print(x) for x in y: print(x) 

Šio pranašumo lyginant su kažkuo panašiu list(generator) yra tai, kad O(1) yra erdvės sudėtingumas, o list(generator) yra O(n) . Trūkumas yra tas, kad jei turite prieigą tik prie generatoriaus, bet ne su generatoriumi sukurtos funkcijos, jūs negalite naudoti šio metodo. Pvz., Gali pasirodyti pagrįsta atlikti šiuos veiksmus, tačiau ji neveiks.

 g = (x * x for x in range(5)) squares = ReusableGenerator(lambda: g) for x in squares: print(x) for x in squares: print(x) 
5
19 сент. Atsakymas, kurį pateikė michaelsnowden Sep 19 2016-09-19 06:04 '16 at 6:04 2016-09-19 06:04

Jei „GrzegorzOledzki“ atsakymas yra nepakankamas, galite naudoti send() kad pasiektumėte savo tikslą. Daugiau informacijos apie patobulintus generatorius ir išvesties išraiškas žr. PEP-0342 .

UPDATE: žr. itertools.tee() . Jame yra keletas pirmiau minėtų priežasčių, palyginti su atminties apdorojimu, tačiau ji gali išsaugoti tam tikrą atmintį, paprasčiausiai išsaugodama generatoriaus rezultatus list ; Tai priklauso nuo generatoriaus naudojimo.

4
13 авг. atsakymą pateikė Hank Gay 13 rug. 2009-08-13 14:17 '09, 14:17, 2009-08-13 14:17

Jei nustatysite savo generatoriaus funkciją ir norite, kad gautas generatorius būtų paleistas iš naujo, čia galite rasti rūšiavimo fragmentą, kuris gali būti patogus:

 import copy def generator(i): yield from range(i) g = generator(10) print(list(g)) print(list(g)) class GeneratorRestartHandler(object): def __init__(self, gen_func, argv, kwargv): self.gen_func = gen_func self.argv = copy.copy(argv) self.kwargv = copy.copy(kwargv) self.local_copy = iter(self) def __iter__(self): return self.gen_func(*self.argv, **self.kwargv) def __next__(self): return next(self.local_copy) def restartable(g_func: callable) -> callable: def tmp(*argv, **kwargv): return GeneratorRestartHandler(g_func, argv, kwargv) return tmp @restartable def generator2(i): yield from range(i) g = generator2(10) print(next(g)) print(list(g)) print(list(g)) print(next(g)) 

išėjimai:

 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [] 0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 1 
2
25 февр. Ben Usman atsakymas vasario 25 d 2017-02-25 00:41 '17 0:41 2017-02-25 00:41

Nėra jokių iš naujo nustatytų iteratorių. Paprastai iteratorius atsiranda, kai ji kartojasi per next() funkciją. Vienintelis būdas yra padaryti atsarginę kopiją prieš pakartojant iteratorių. Patikrinkite toliau.

Iteratoriaus objekto su elementais nuo 0 iki 9 kūrimas

 i=iter(range(10)) 

Iteracija per šią () funkciją, kuri pasirodo

 print(next(i)) 

Iteratorio objekto konvertavimas į sąrašą

 L=list(i) print(L) output: [1, 2, 3, 4, 5, 6, 7, 8, 9] 

todėl elementas 0 jau iššoko. Be to, visi elementai rodomi, kai sąraše pateikiame iteratorių.

 next(L) Traceback (most recent call last): File "<pyshell#129>", line 1, in <module> next(L) StopIteration 

Todėl prieš paleisdami iteraciją reikia konvertuoti iteratorių į atsarginių kopijų sąrašus. Sąrašas gali būti konvertuojamas į iteratorių naudojant iter(<list-object>)

1
04 июня '17 в 19:35 2017-06-04 19:35 atsakymą pateikė Amalraj Victory birželio 17 d. 19:35 19:35 19:35

oficialių dokumentų apie tee :

Apskritai, jei vienas iteratorius naudoja daugumą arba visus duomenis prieš pradedant kitą iteratorių, sąrašo () vietoj tee () greičiau naudoti.

Todėl geriau naudoti list(iterable) o ne jūsų atveju.

1
08 сент. Shubham Chaudhary atsakymas. 2016-09-08 16:59 '16, 16:59 pm 2016-09-08 16:59

Dabar galite naudoti more_itertools.seekable (trečiosios šalies įrankį), kuris leidžia iš naujo nustatyti iteratorius.

Įdiekite per > pip install more_itertools

 import more_itertools as mit y = mit.seekable(FunctionWithYield()) for x in y: print(x) y.seek(0) # reset iterator for x in y: print(x) 

Pastaba: atminties suvartojimas didėja, kai įsijungia iteratorius, todėl būkite atsargūs su dideliais iteracijomis.

0
06 дек. atsakymas duotas 06 d. 2017-12-06 05:23 '17, 5:23, 2017-12-06 05:23

Aš nesu įsitikinęs, ką jūs naudojote brangių treniruočių pagalba, bet manau, kad jūs iš tikrųjų turite

 data = ... # Expensive computation y = FunctionWithYield(data) for x in y: print(x) #here must be something to reset 'y' # this is expensive - data = ... # Expensive computation # y = FunctionWithYield(data) for x in y: print(x) 

Jei taip, kodėl gi ne naudoti data ?

0
Atsakymą pateikė ilya n. Rugpjūčio 13 d 2009-08-13 15:38 '09, 15:38, 2009-08-13 15:38

Galite nustatyti funkciją, kuri grąžina generatorių.

 def f(): def FunctionWithYield(generator_args): code here... return FunctionWithYield 

Dabar galite padaryti tiek kartų, kiek norite:

 for x in f()(generator_args): print(x) for x in f()(generator_args): print(x) 
0
07 марта '16 в 12:55 2016-03-07 12:55 atsakymas pateikiamas SMeznaric kovo 7 d. 16 d. 12:55 2016-03-07 12:55

Na, jūs sakote, kad norite kelis kartus skambinti generatoriui, bet inicijavimas yra brangus ... Kaip apie tai kažką?

 class InitializedFunctionWithYield(object): def __init__(self): # do expensive initialization self.start = 5 def __call__(self, *args, **kwargs): # do cheap iteration for i in xrange(5): yield self.start + i y = InitializedFunctionWithYield() for x in y(): print x for x in y(): print x 

Arba galite tiesiog sukurti savo klasę, kuri seka iteratoriaus protokolą, ir apibrėžia tam tikrą atstatymo funkciją.

 class MyIterator(object): def __init__(self): self.reset() def reset(self): self.i = 5 def __iter__(self): return self def next(self): i = self.i if i > 0: self.i -= 1 return i else: raise StopIteration() my_iterator = MyIterator() for x in my_iterator: print x print 'resetting...' my_iterator.reset() for x in my_iterator: print x 

https://docs.python.org/2/library/stdtypes.html#iterator-types http://anandology.com/python-practice-book/iterators.html

0
15 нояб. atsakymas pateiktas tvt173 15 nov. 2016-11-15 20:03 '16 at 8:03 pm 2016-11-15 20:03

Tai galima padaryti naudojant kodo objektą. Čia yra pavyzdys.

 code_str="y=(a for a in [1,2,3,4])" code1=compile(code_str,'<string>','single') exec(code1) for i in y: print i 

1 2 3 4

 for i in y: print i exec(code1) for i in y: print i 

1 2 3 4

-3
27 авг. atsakymas pateikiamas Olegos 27 d. 2013-08-27 04:56 '13, 4:56, 2013-08-27 04:56

Kiti klausimai apie žymių arba Užduoti klausimą