El orden de iteración establecido varía de una ejecución a otra

¿Por qué el orden de iteración de un conjunto de Python (con el mismo contenido) varía de una ejecución a otra, y cuáles son mis opciones para que sea consistente de una ejecución a otra?

Entiendo que el orden de iteración de un conjunto de Python es arbitrario. Si pongo ‘a’, ‘b’ y ‘c’ en un conjunto y luego los itero, pueden volver a salir en cualquier orden.

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

Desafortunadamente, esto rompe una de mis pruebas automatizadas, que simplemente compara la salida de dos ejecuciones de mi progtwig. No me importa el orden real, pero me gustaría que sea consistente de una ejecución a otra.

La mejor solución que he encontrado es:

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

¿Hay una solución más simple?

Nota: he encontrado preguntas similares en StackOverlow, pero ninguna que aborde este problema específico de obtener los mismos resultados de una ejecución a otra.

Lo que quieres no es posible. Arbitrario significa arbitrario.

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

Use el operador symmetric_difference (^) en sus dos conjuntos para ver si hay alguna diferencia:

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

La razón por la que el orden de iteración establecido cambia de ejecución a ejecución parece ser que Python usa la aleatorización de hash por defecto. (Consulte la opción de comando -R .) Por lo tanto, establecer la iteración no solo es arbitraria (debido al 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 de ejecución a ejecución significa que la iteración del conjunto sigue siendo arbitraria, pero ahora es determinista, que era la propiedad deseada.

La asignación aleatoria de semillas de hash es una medida de seguridad que dificulta que un adversario alimente insumos que causarán un comportamiento patológico (por ejemplo, al crear numerosas colisiones de hash). Para las pruebas unitarias, esto no es una preocupación, por lo que es razonable anular el valor de hash al ejecutar pruebas.

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. Por lo tanto, puede crear dos conjuntos diferentes, con 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.

Su pregunta se transformó en dos preguntas: A) cómo comparar “el resultado de dos ejecuciones” 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 es apropiado. Contestaré A.

En mi humilde opinión, usar una lista ordenada en su caso no es una solución muy limpia. Debe decidir si le importa el orden de iteración de una vez por todas y utilizar 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 conjuntos de python2 , conjuntos de python3 .

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 es importante para los usuarios de su biblioteca, en cuyo caso el uso del tipo de conjunto probablemente no fue apropiado para comenzar. Dicho de otra manera, no está claro qué quiere decir exactamente al “comparar la salida de dos ejecuciones” y por qué quiere hacer eso.

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

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

Contrariamente a los conjuntos, las listas siempre tienen un orden garantizado, por lo que puede tirar el conjunto y usar la lista.