2012-04-02 20 views
21

Mi domando circa l'uso di == quando si confrontano i due generatoriConfrontando due generatori in Python

Ad esempio:

x = ['1','2','3','4','5'] 

gen_1 = (int(ele) for ele in x) 
gen_2 = (int(ele) for ele in x) 

gen_1 e gen_2 sono uguali per tutti gli scopi pratici, e ancora quando li paragono:

>>> gen_1 == gen_2 
False 

La mia ipotesi qui è che == qui viene trattato come is normalmente è, e poiché gen_1 e gen_2 si trovano in luoghi diversi nella memoria:

>>> gen_1 
<generator object <genexpr> at 0x01E8BAA8> 
>>> gen_2 
<generator object <genexpr> at 0x01EEE4B8> 

loro confronto restituisce False. Ho ragione su questa ipotesi? E ogni altra intuizione è benvenuta.

E btw, io so come confrontare due generatori:

>>> all(a == b for a,b in zip(gen_1, gen_2)) 
True 

o anche

>>> list(gen_1) == list(gen_2) 
True 

Ma se c'è un modo migliore, mi piacerebbe sapere.

+5

Pensa all'espressione di un generatore come se fosse una funzione, non come una lista. – agf

+1

una volta confrontati i generatori li esaurisci e poi diventano vuoti –

risposta

12

Hai ragione con la tua ipotesi: il fallback per il confronto di tipi che non definiscono == è un confronto basato sull'identità dell'oggetto.

Un modo migliore per confrontare i valori che generano sarebbe

from itertools import izip_longest, tee 
sentinel = object() 
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel)) 

Questo può effettivamente cortocircuitare senza necessariamente dover guardare tutti i valori. Come sottolineato da Larsmans nei commenti, non possiamo usare izip() perché potrebbe dare risultati errati se i generatori producono un numero diverso di elementi - izip() si fermerà sull'iteratore più breve. Usiamo una nuova istanza object come valore di riempimento per izip_longest(), poiché le istanze object vengono anche confrontate dall'identità dell'oggetto, pertanto è garantito il confronto tra non uguale a tutto il resto.

Si noti che non è possibile confrontare i generatori senza modificarne lo stato. È possibile memorizzare gli elementi che sono stati consumati, se ne avete bisogno in seguito:

gen_1, gen_1_teed = tee(gen_1) 
gen_2, gen_2_teed = tee(gen_2) 
all(a == b for a, b in izip_longest(gen_1, gen_2, fillvalue=sentinel)) 

Questo darà lasciare lo stato di gen_1 e gen_2 sostanzialmente invariato.Tutti i valori consumati da all() vengono memorizzati all'interno dell'oggetto tee.

A quel punto, potreste chiedervi se valga davvero la pena utilizzare i generatori pigri per l'applicazione a portata di mano - potrebbe essere meglio semplicemente convertirli in elenchi e lavorare con gli elenchi.

+0

'izip' ha lo stesso problema di' zip': fallisce quando i generatori non generano un numero uguale di elementi. –

+0

@larsmans: Grazie, ho appena notato! –

+0

Grazie mille Sven! – Akavall

7

Poiché i generatori generano i propri valori su richiesta, non esiste alcun modo per "confrontarli" senza effettivamente consumando. E se i tuoi generatori generano una sequenza infinita di valori, un tale test di uguaglianza che proponi sarebbe inutile.

4

Per fare un confronto tra due generatori di due generatori come con elenchi e altri contenitori, Python dovrebbe consumarli entrambi completamente (beh, quello più corto, comunque). Penso che sia bene che tu lo faccia esplicitamente, specialmente perché uno o l'altro può essere infinito.

5

== è lo stesso di is su due generatori, perché è l'unico controllo che è possibile effettuare senza modificare il loro stato e quindi perdere elementi.

list(gen_1) == list(gen_2) 

è il modo affidabile e generale di confrontare due generatori finiti (ma ovviamente consuma entrambi); il vostro zip soluzione basata su fallisce quando non generano un numero uguale di elementi:

>>> list(zip([1,2,3,4], [1,2,3])) 
[(1, 1), (2, 2), (3, 3)] 
>>> all(a == b for a, b in zip([1,2,3,4], [1,2,3])) 
True 

La soluzione basata su list fallisce ancora quando uno generatore genera un numero infinito di elementi. Puoi escogitare una soluzione alternativa, ma quando entrambi i generatori sono infiniti, puoi escogitare solo uno semi-algorithm per la non uguaglianza.

+0

Grazie per aver segnalato l'errore nella mia soluzione basata su zip. – Akavall