„Python“: generatoriaus išraiška

Ar „Python“ yra skirtumas tarp generatoriaus objekto kūrimo naudojant generatoriaus išraišką, palyginti su naudos ataskaitos naudojimu ?

Naudojant išėjimą:

 def Generator(x, y): for i in xrange(x): for j in xrange(y): yield(i, j) 

Naudojant generatoriaus išraišką:

 def Generator(x, y): return ((i, j) for i in xrange(x) for j in xrange(y)) 

Abi funkcijos grąžina generatoriaus objektus, gaminančius, pavyzdžiui, rinkmenas. (0,0), (0,1) ir tt

Bet kokios naudos iš vieno ar kito? Mintys?


Ačiū visiems! Šie atsakymai turi daug puikios informacijos ir tolesnių nuorodų!

77
03 янв. nustatyti cschol 03 jan. 2010-01-03 19:09 '10, 19:09, 2010-01-03 19:09
@ 8 atsakymai

Yra tik nedideli skirtumai. Galite naudoti dis modulį tokiems dalykams išmokti sau.

Redaguoti: Mano pirmoji versija suaktyvino interaktyviosios pagalbos modulių srityje sukurtą generatoriaus išraišką. Tai šiek tiek skiriasi nuo OP versijos, naudojamos funkcijos viduje. Aš tai pakeitiau, kad atitiktų konkretų atvejį.

Kaip matote žemiau, „derlingumo“ generatorius (pirmasis atvejis) turi tris papildomas konfigūravimo instrukcijas, tačiau nuo pirmojo FOR_ITER jie skiriasi tik vienu atžvilgiu: „išeiga“ metodas naudoja LOAD_FAST vietoj LOAD_DEREF kilpos viduje. LOAD_DEREF „lėčiau“ nei LOAD_FAST , todėl versija „produkcija“ šiek tiek greičiau nei generatoriaus išraiška yra pakankamai didelė x reikšmei (išorinė kilpa), nes y vertė yra įkeliama šiek tiek greičiau kiekviename leidime. Mažesnėms x reikšmėms jis bus šiek tiek lėčiau, nes papildomas pridėjimo kodas yra.

Taip pat gali būti patartina nurodyti, kad generatoriaus išraiška dažniausiai naudojama kodo viduje, o ne su tokia funkcija. Tai leistų šiek tiek mažesnę įrenginio viršutinę dalį ir pagreitinti generatoriaus išraišką mažesnėms ciklų vertėms, net jei LOAD_FAST suteikė pranašumą kitaip.

Bet kokiu atveju, skirtumo tarp veiklos rezultatų pakaktų tam, kad būtų pagrįstas sprendimas tarp šio ar kito. Skaitymas yra skaičiuojamas daug daugiau, todėl naudokite tai, kas atrodo labiausiai suprantama situacijai.

 >>> def Generator(x, y): ... for i in xrange(x): ... for j in xrange(y): ... yield(i, j) ... >>> dis.dis(Generator) 2 0 SETUP_LOOP 54 (to 57) 3 LOAD_GLOBAL 0 (xrange) 6 LOAD_FAST 0 (x) 9 CALL_FUNCTION 1 12 GET_ITER >> 13 FOR_ITER 40 (to 56) 16 STORE_FAST 2 (i) 3 19 SETUP_LOOP 31 (to 53) 22 LOAD_GLOBAL 0 (xrange) 25 LOAD_FAST 1 (y) 28 CALL_FUNCTION 1 31 GET_ITER >> 32 FOR_ITER 17 (to 52) 35 STORE_FAST 3 (j) 4 38 LOAD_FAST 2 (i) 41 LOAD_FAST 3 (j) 44 BUILD_TUPLE 2 47 YIELD_VALUE 48 POP_TOP 49 JUMP_ABSOLUTE 32 >> 52 POP_BLOCK >> 53 JUMP_ABSOLUTE 13 >> 56 POP_BLOCK >> 57 LOAD_CONST 0 (None) 60 RETURN_VALUE >>> def Generator_expr(x, y): ... return ((i, j) for i in xrange(x) for j in xrange(y)) ... >>> dis.dis(Generator_expr.func_code.co_consts[1]) 2 0 SETUP_LOOP 47 (to 50) 3 LOAD_FAST 0 (.0) >> 6 FOR_ITER 40 (to 49) 9 STORE_FAST 1 (i) 12 SETUP_LOOP 31 (to 46) 15 LOAD_GLOBAL 0 (xrange) 18 LOAD_DEREF 0 (y) 21 CALL_FUNCTION 1 24 GET_ITER >> 25 FOR_ITER 17 (to 45) 28 STORE_FAST 2 (j) 31 LOAD_FAST 1 (i) 34 LOAD_FAST 2 (j) 37 BUILD_TUPLE 2 40 YIELD_VALUE 41 POP_TOP 42 JUMP_ABSOLUTE 25 >> 45 POP_BLOCK >> 46 JUMP_ABSOLUTE 6 >> 49 POP_BLOCK >> 50 LOAD_CONST 0 (None) 53 RETURN_VALUE 
68
03 янв. Peterio Hanseno atsakymas dėl sausio 03 d 2010-01-03 20:17 '10, 20:17, 2010-01-03 20:17

Šiame pavyzdyje tai nėra. Tačiau yield gali būti naudojamas sudėtingesnėms konstrukcijoms - pavyzdžiui , jis taip pat gali priimti skambinančiojo vertes ir pakeisti srautą. Skaitykite daugiau apie PEP 342 (tai įdomi technika, kurią verta žinoti).

Bet kokiu atveju geriausias patarimas yra naudoti viską, kas geriau suprantama jūsų poreikiams .

PS Čia yra paprastas pavyzdys iš Dave Bezley :

 def grep(pattern): print "Looking for %s" % pattern while True: line = (yield) if pattern in line: print line, # Example use if __name__ == '__main__': g = grep("python") g.next() g.send("Yeah, but no, but yeah, but no") g.send("A series of tubes") g.send("python generators rock!") 
35
03 янв. Atsakymą pateikė Eli Bendersky sausio 03 d 2010-01-03 19:13 '10, 19:13, 2010-01-03 19:13

Paprastų ciklų, kuriuos galite įterpti į generatoriaus išraišką, tipui nėra skirtumo. Tačiau išvestis gali būti naudojama kuriant generatorius, atliekančius daug sudėtingesnį apdorojimą. Štai paprastas Fibonacci sekos generavimo pavyzdys:

 >>> def fibgen(): ... a = b = 1 ... while 1: ... yield a ... a, b = b, a+b >>> list(itertools.takewhile((lambda x: x<100), fibgen())) [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89] 
17
03 янв. Dave Kirby atsakymas sausio 03 d 2010-01-03 19:30 '10 19:30 val. 2010-01-03 19:30

yield gražus, jei išraiška yra sudėtingesnė nei tik įdėtos kilpos. Be kitų dalykų, galite grąžinti specialią pirmąją arba specialiąją paskutinę vertę. Apsvarstykite:

 def Generator(x): for i in xrange(x): yield(i) yield(None) 
8
03 янв. Atsakymas duotas Tor Valamo 03 jan. 2010-01-03 19:13 '10, 19:13, 2010-01-03 19:13

Naudojant pažymėkite skirtumą tarp generatoriaus objekto ir generatoriaus funkcijos.

Generatoriaus objektas naudojamas tik vieną kartą, skirtingai nuo generatoriaus funkcijos, kurią galima pakartotinai naudoti kiekvieną kartą, kai jį dar kartą skambinate, nes jis grąžina naują generatoriaus objektą.

Paprastai generatoriaus išraiškos paprastai naudoja „neapdorotą“, nepadengdamos jų į funkciją, ir grąžina generatoriaus objektą.

pavyzdžiui:

 def range_10_gen_func(): x = 0 while x < 10: yield x x = x + 1 print(list(range_10_gen_func())) print(list(range_10_gen_func())) print(list(range_10_gen_func())) 

kurios išvestys:

 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 

Palyginti su šiek tiek skirtingais naudojimo būdais:

 range_10_gen = range_10_gen_func() print(list(range_10_gen)) print(list(range_10_gen)) print(list(range_10_gen)) 

kurios išvestys:

 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [] [] 

Ir palyginkite su generatoriaus išraiška:

 range_10_gen_expr = (x for x in range(10)) print(list(range_10_gen_expr)) print(list(range_10_gen_expr)) print(list(range_10_gen_expr)) 

kurie taip pat išeina:

 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [] [] 
8
12 янв. Craig McQueen atsakymas, sausio 12 d 2010-01-12 09:26 '10, 9:26 val. 2010-01-12 09:26

Kai galvojame apie iteratorius, itertools modulis:

... standartizuoja pagrindinius greito, atminties efektyvumo įrankius, kurie yra naudingi patys arba kartu. Kartu jie sudaro „iteracinę algebrą“, kuri leidžia jums sukurti specializuotus įrankius glaustai ir efektyviai gryname „Python“.

Jei norite atlikti našumą, apsvarstykite itertools.product(*iterables[, repeat])

Dekartinis įvesties iteracijos produktas.

Lygiavertė įdėtinėms kilpoms generatoriaus išraiška. Pavyzdžiui, product(A, B) grąžina tą patį ((x,y) for x in A for y in B) .

 >>> import itertools >>> def gen(x,y): ... return itertools.product(xrange(x),xrange(y)) ... >>> [t for t in gen(3,2)] [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)] >>> 
5
03 янв. atsakymas suteiktas gimel 03 jan. 2010-01-03 19:21 '10, 19:21, 2010-01-03 19:21

Taip, yra skirtumas.

Generatoriaus išraiška (x for var in expr) , iter(expr) yra vadinama, kai sukuriama išraiška.

Naudojant def ir yield generatorius sukuriamas, kaip:

 def my_generator(): for var in expr: yield x g = my_generator() 

iter(expr) dar neskambinama. Jis bus vadinamas tik tada, kai jis kartojamas g (ir negali būti visai vadinamas).

Šio iteratoriaus pavyzdys:

 from __future__ import print_function class CountDown(object): def __init__(self, n): self.n = n def __iter__(self): print("ITER") return self def __next__(self): if self.n == 0: raise StopIteration() self.n -= 1 return self.n next = __next__ # for python2 

Šis kodas yra:

 g1 = (i ** 2 for i in CountDown(3)) # immediately prints "ITER" print("Go!") for x in g1: print(x) 

a

 def my_generator(): for i in CountDown(3): yield i ** 2 g2 = my_generator() print("Go!") for x in g2: # "ITER" is only printed here print(x) 

Kadangi daugelis iteratorių nedaro daug __iter__ , tai lengva praleisti šį elgesį. Realaus pasaulio pavyzdys yra „Django QuerySet , kuris QuerySet duomenis __iter__ ir data = (f(x) for x in qs) gali užtrukti ilgai, o def g(): for x in qs: yield f(x) , po to data=g() , iškart grįš.

Daugiau informacijos ir formalaus apibrėžimo žr. PEP 289 - Generatoriaus išraiškos .

2
18 мая '17 в 2:01 2017-05-18 02:01 Atsakymas įduotas Udui gegužės 18 d. 17 d

Yra skirtumas, kuris gali būti svarbus kai kuriuose kontekstuose, kurie dar nebuvo pažymėti. Naudojimasis derliu neleidžia naudoti return kažkam kitam, nei netiesiogiai didinti StopIteration (ir dalykų, susijusių su coroutine) .

Tai reiškia, kad šis kodas yra prastai suformuotas (ir maitindamas ją vertėjui suteiks jums AttributeError ):

 class Tea: """With a cloud of milk, please""" def __init__(self, temperature): self.temperature = temperature def mary_poppins_purse(tea_time=False): """I would like to make one thing clear: I never explain anything.""" if tea_time: return Tea(355) else: for item in ['lamp', 'mirror', 'coat rack', 'tape measure', 'ficus']: yield item print(mary_poppins_purse(True).temperature) 

Kita vertus, šis kodas veikia kaip žavesys:

 class Tea: """With a cloud of milk, please""" def __init__(self, temperature): self.temperature = temperature def mary_poppins_purse(tea_time=False): """I would like to make one thing clear: I never explain anything.""" if tea_time: return Tea(355) else: return (item for item in ['lamp', 'mirror', 'coat rack', 'tape measure', 'ficus']) print(mary_poppins_purse(True).temperature) 
0
17 нояб. Atsakymą pateikė Adrien lapkričio 17 d. 2016-11-17 03:07 '16 at 3:07 2016-11-17 03:07

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