2010-10-03 16 views
16

¿Por qué el orden de iteración de un conjunto de Python (con los mismos contenidos) varía de ejecución a ejecución y cuáles son mis opciones para que sea consistente desde la ejecución hasta la ejecución?Establecer el orden de iteración varía de ejecución a ejecución

Entiendo que el orden de iteración para un conjunto de Python es arbitrario. Si pongo 'a', 'b' y 'c' en un conjunto y luego los repito, pueden volver a aparecer en cualquier orden.

Lo que he observado es que el orden sigue siendo el mismo dentro de una ejecución del programa. Es decir, si mi programa itera el mismo conjunto dos veces seguidas, obtengo el mismo orden las dos veces. Sin embargo, si ejecuto el programa dos veces seguidas, el orden cambia de ejecución a ejecución.

Desafortunadamente, esto rompe una de mis pruebas automáticas, que simplemente compara la salida de dos ejecuciones de mi programa. No me importa el orden real, pero me gustaría que sea consistente desde la ejecución hasta la ejecución.

La mejor solución que he llegado con es:

  1. Copia el conjunto a una lista.
  2. Aplicar una ordenación arbitraria a la lista.
  3. Iterate la lista en lugar del conjunto.

¿Existe una solución más simple?

Nota: He encontrado preguntas similares en StackOverlow, pero ninguna que resuelva este problema específico de obtener los mismos resultados de la ejecución para ejecutarse.

+0

Si lo que está probando es que "el programa genera lo mismo dos veces", la opción de lista ordenada es su mejor opción. Si lo que está probando es que "el programa crea el mismo conjunto en ambas ocasiones", tendrá que hacer una comparación de conjuntos (reduciendo la producción de ambas ejecuciones, desatornillando la salida de ambas y estableciendo comparaciones entre ellas, o algo moralmente equivalente). –

+0

@Russell: Tengo pruebas unitarias que verifican los contenidos establecidos. Pero también tengo esta prueba que compara la salida de dos carreras como una verificación de cordura. El resultado depende, en parte, del orden de los elementos en un conjunto, pero solo de forma indirecta. –

Respuesta

10

Lo que desea no es posible. Arbitrario significa arbitrario.

Mi solución sería la misma que la tuya, tienes que ordenar el conjunto si deseas poder compararlo con otro.

+5

Supongo que asumí que arbitrario significaba que dependería de los contenidos, no de la fase de la luna. –

+0

Bueno, no es arbitrario, entonces no es determinista. Probablemente haya una manera de poder determinar cuál será el orden en el conjunto, pero apostaría que eso es más problemático de lo que vale. Busque un conjunto ordenado, o similar en python ... – JoshD

+5

Incluso si fuera consistente desde la ejecución hasta la ejecución, no habría garantía de que sería consistente de máquina a máquina, versión de python a versión de Python, cpython contra jython, etc. –

-1

Al contrario de los juegos, las listas siempre tienen un orden garantizado, por lo que puede tirar el juego y usar la lista.

+0

Sí, pero estoy creando los conjuntos usando varias operaciones de conjunto (uniones, intersecciones, etc.). Esas son menos eficientes como listas. –

4

El orden de iteración del conjunto depende no solo de su contenido, sino también del orden en que se insertaron los elementos en el conjunto y de si hubo eliminaciones en el camino. De modo que puede crear dos conjuntos diferentes, utilizando diferentes inserciones y eliminaciones, y terminar con el mismo conjunto al final, pero con diferentes órdenes de iteración.

Como han dicho otros: si te importa el orden del conjunto, debes crear una lista ordenada a partir de él.

+0

Ejecutar mi programa dos veces seguidas con la misma entrada implica la misma secuencia de inserciones, eliminaciones y operaciones de conjunto, pero el orden de iteración aún cambia. Es como si hubiera algo más involucrado, como la hora del día, la identificación del proceso u otra cosa que varíe de una ejecución a otra. –

+4

Thomas Wouters señala en su comentario anterior que algunas clases usan id() en la función hash, lo que significa que el hash del objeto depende de su dirección de memoria, y quién sabe qué podría hacer eso diferente. Si está utilizando sus propias clases, puede escribir su propia función __hash__ para deshacerse de parte de esa indeterminación, pero probablemente sea mejor que simplemente clasifique los resultados de todos modos. –

11

Uso del symmetric_difference (^) del operador en sus dos juegos para ver si hay algunas diferencias:

In [1]: s1 = set([5,7,8,2,1,9,0]) 
In [2]: s2 = set([9,0,5,1,8,2,7]) 
In [3]: s1 
Out[3]: set([0, 1, 2, 5, 7, 8, 9]) 
In [4]: s2 
Out[4]: set([0, 1, 2, 5, 7, 8, 9]) 
In [5]: s1^s2 
Out[5]: set() 
+0

Eso está bien para comparar directamente los conjuntos. En mis pruebas, sin embargo, estoy buscando una manera simple de simplemente comparar la salida de una ejecución a otra, y esa salida se ve afectada por el orden de iteración. –

2

Su pregunta transforma en dos preguntas: a) ¿Cómo comparar "la salida de dos carreras" en su caso específico; B) ¿Cuál es la definición del orden de iteración en un conjunto? Tal vez debería distinguirlos y publicar B) como una nueva pregunta, si corresponde. Voy a contestar A.

En mi humilde opinión, utilizar una lista ordenada en su caso no es una solución muy clara. Debe decidir si desea el pedido de iteración de una vez por todas y usar la estructura adecuada.

O bien 1) desea comparar los dos conjuntos para ver si tienen el mismo contenido, independientemente del orden. Entonces el operador simple == en conjuntos parece apropiado.Ver python2 sets, python3 sets.

O 2) desea comprobar si los elementos se insertaron en el mismo orden. Pero esto parece razonable solo si el orden de inserción de alguna manera le importa a los usuarios de su biblioteca, en cuyo caso usar el tipo de conjunto probablemente fue inapropiado para empezar. Dicho de otra manera, no está claro a qué se refiere exactamente al "comparar la salida de dos carreras" y por qué quiere hacer eso.

En todos los casos, dudo que una lista ordenada sea apropiada aquí.

2

La razón por la que la orden de iteración establecida cambia de ejecución a ejecución parece ser porque Python utiliza la asignación aleatoria de la semilla hash de forma predeterminada. (Consulte la opción de comando -R). Por lo tanto, la iteración establecida no solo es arbitraria (debido a hashing), sino también no determinista (debido a la semilla aleatoria).

Puede anular la semilla aleatoria con un valor fijo configurando la variable de entorno PYTHONHASHSEED para el intérprete. Usar la misma semilla desde la ejecución hasta la ejecución significa que la iteración establecida sigue siendo arbitraria, pero ahora es determinista, que era la propiedad deseada.

La aleatorización de semillas de hash es una medida de seguridad para dificultar que un adversario alimente entradas que causarán un comportamiento patológico (por ejemplo, creando numerosas colisiones hash). Para las pruebas unitarias, esto no es una preocupación, por lo que es razonable anular el hash seed mientras se ejecutan las pruebas.

+0

La adición de hash aleatorio a Python no se produjo hasta 2012. – pydsigner

+0

@pydsigner: Eso es interesante, ya que de hecho este problema resuelve el problema que estaba enfrentando. Volví a este proyecto el otoño pasado, y la configuración de PYTHONHASHSEED ha hecho que el resultado de mis pruebas sea consistente desde la ejecución hasta la ejecución. –

+0

De hecho, es interesante ... [2.6.8] (https://docs.python.org/2.7/using/cmdline.html#envvar-PYTHONHASHSEED) y [3.2.3] (https: //docs.python. org/3.3/using/cmdline.html # envvar-PYTHONHASHSEED) fueron las versiones donde se introdujo esto. – pydsigner

0

Puede establecer que el resultado esperado sea también un conjunto. Y comprueba si esos dos conjuntos son iguales usando ==.

Cuestiones relacionadas