2012-04-02 24 views
21

Me pregunto sobre el uso de == cuando se comparan dos generadoresLa comparación de dos generadores en Python

Por ejemplo:

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

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

gen_1 y gen_2 son los mismos para todos los propósitos prácticos, y sin embargo, cuando los comparo:

>>> gen_1 == gen_2 
False 

Supongo que es == aquí se trata como is normalmente, y desde gen_1 y gen_2 están situados en diferentes lugares de la memoria:

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

su comparación se evalúa como False. ¿Estoy en lo cierto en esta suposición? Y cualquier otra idea es bienvenida.

Y por cierto, yo no sé cómo comparar dos generadores:

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

o incluso

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

Pero si hay una manera mejor, me gustaría saber.

+5

Piense en una expresión de generador como si fuera una función, no como una lista. – agf

+1

una vez que compara los generadores los agota y luego se vuelven vacíos –

Respuesta

12

Tiene razón con su conjetura: el retroceso para la comparación de tipos que no definen == es una comparación basada en la identidad del objeto.

Una mejor manera de comparar los valores que generan sería

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

Esto puede realmente corto circuito sin necesidad de tener que mirar a todos los valores. Como señala larsmans en los comentarios, no podemos usar izip() aquí ya que podría dar resultados incorrectos si los generadores producen un número diferente de elementos: izip() se detendrá en el iterador más corto. Usamos una instancia recién creada object como valor de relleno para izip_longest(), ya que las instancias object también se comparan por identidad de objeto, por lo que sentinel se garantiza que no se puede comparar con todo lo demás.

Tenga en cuenta que no hay forma de comparar generadores sin cambiar su estado. Se podría almacenar los artículos que se han consumido, si los necesita más adelante:

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)) 

Esto le dará a salir del estado de gen_1 y gen_2 esencialmente sin cambios.Todos los valores consumidos por all() se almacenan dentro del objeto tee.

En ese momento, puede preguntarse si realmente vale la pena usar generadores perezosos para la aplicación en cuestión; podría ser mejor simplemente convertirlos en listas y trabajar con las listas.

+0

'izip' tiene el mismo problema que' zip': falla cuando los generadores no generan la misma cantidad de elementos. –

+0

@larsmans: ¡Gracias, acabo de darme cuenta! –

+0

Muchas gracias Sven! – Akavall

7

Dado que los generadores generan sus valores bajo demanda, no hay ninguna manera de "compararlos" sin que realmente consuman ellos. Y si tus generadores generan una secuencia infinita de valores, tal prueba de igualdad como propones sería inútil.

4

Con el fin de hacer una comparación de dos generadores como con las listas y otros contenedores, Python tendría que consumirlos por completo (bueno, el más corto, de todos modos). Creo que es bueno que lo hagas explícitamente, especialmente porque uno u otro puede ser infinito.

5

== es de hecho lo mismo que is en dos generadores, porque esa es la única verificación que se puede hacer sin cambiar su estado y perder elementos.

list(gen_1) == list(gen_2) 

es la manera confiable y general de comparar dos generadores finitos (pero obviamente consume ambos); su solución basada en zip falla cuando no generan un número igual de elementos:

>>> 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 solución basada en list tampoco funciona cuando cualquiera generador genera un número infinito de elementos. Puede diseñar una solución para eso, pero cuando ambos generadores son infinitos, solo puede diseñar un semi-algorithm para la no igualdad.

+0

Gracias por señalar la falacia en mi solución basada en zip. – Akavall