2012-05-03 11 views
13

Entiendo que los elementos de un conjunto de pitones no están ordenados. Llamar al método pop devuelve un elemento arbitrario; Estoy bien con eso.En python, ¿es set.pop() determinista?

Lo que me pregunto es si pop devolverá SIEMPRE el mismo elemento cuando el conjunto tenga el mismo historial. Dentro de una versión de python, por supuesto, no me importa si las diferentes versiones/implementaciones de python hacen lo suyo. En particular, estoy preguntando sobre Python 2.7. Es una cuestión de implementación más que de api en este caso.

Uso mucho los conjuntos en un generador de mazmorra de procedimiento para un juego, y me gustaría que el resultado sea determinista para una semilla determinada.

+1

Relacionados: http://stackoverflow.com/questions/3949310/how-is-cpythons-set-implemented y http://svn.python.org/view/python/trunk/Objects/setobject.c?view = marcado – ChristopheD

+1

¿Por qué no probarlo/ver la fuente? – Marcin

+0

@delnan "En particular, estoy preguntando por python 2.7. Es una cuestión de implementación más que de api en este caso". Por lo tanto, no es necesario probar varias versiones, o versiones futuras, como sugiere. Parece que has imaginado un requisito para la portabilidad y la eternidad. – Marcin

Respuesta

18

La respuesta en general es no. La fuente python que @Christophe y @Marcin (un) señalan útilmente muestra que los elementos aparecen en el orden en que aparecen en la tabla hash. Por lo tanto, orden pop (y presumiblemente orden de iteración) es determinista, pero solo para valores de hash fijos. Ese es el caso de los números, pero no para cuerdas, de acuerdo con el Nota en la documentación de __hash__, que por cierto también se refiere a su pregunta directamente:

Nota por defecto el de hash() los valores de los objetos str, bytes y datetime están "salados" con un valor aleatorio impredecible. Aunque permanecen constantes dentro de un proceso de Python individual, no son predecibles entre invocaciones repetidas de Python.

[...]

Cambio de valores hash afecta al orden de iteración de dicts, juegos y otras asignaciones. Python nunca ha hecho garantías sobre este orden (y generalmente varía entre compilaciones de 32 bits y de 64 bits).

Editar: Como @Marcin señala, el enlace que he citado no se aplica a Python 2. Hash aleatorización became the default with Python 3.3. Python 2.7 no tiene cadena hash intencionadamente no determinista por defecto.

En general, este es un problema para cualquier objeto cuyo hash no es una función repetible de su valor (por ejemplo, si el hash se basa en la dirección de memoria). Pero a la inversa, si define su propio método __hash__ para los objetos en sus conjuntos, puede esperar que se devuelvan en un orden reproducible. (Siempre que el historial y la plataforma del conjunto se mantengan fijos).

+1

Usted se está refiriendo a la documentación para la versión dev de python. Esta pregunta es sobre Python 2.7, y el texto que cita no aparece en el documento correspondiente para esa versión: http://docs.python.org/reference/datamodel.html # object .__ hash__ – Marcin

+0

¡Gracias por descubrir eso! – alexis

4

The documentation no especifica que debe ser determinista, por lo tanto, debe suponer que no lo es.

+2

Dado que la pregunta parece ser sobre una versión específica, no hay necesidad de suponer nada: se puede verificar la fuente y comprobar el comportamiento. – Marcin

6

Internamente, creo que la situación es similar a dict. El orden está determinado por un algoritmo hash, que en algunas situaciones arrojarán los mismos resultados. Pero no deberías depender de eso, ya que una vez que la cantidad de elementos aumenta, el conjunto se encontrará con colisiones (es decir, hashing interno), lo que finalmente conducirá a un orden diferente.

En resumen: No, set.pop() no es determinista. No asuma cualquier orden, ya que la API establece explícitamente, que

un objeto de conjunto es una colección desordenada

1

Si quiere forzar el determinismo, podría intentar algo como

value = min(my_set) 
my_set.remove(value) 
+2

tenga en cuenta que esto es solo determinista cuando min() no es ambiguo. Es posible tener un conjunto extraño con valores distintos donde hay dos o más que son todos menos que todos los demás (y ambos son menos que el otro). no es común en la naturaleza, pero es posible. – ch3ka

+2

Un mejor ejemplo serían los valores que simplemente no se pueden pedir (es decir, que solo se pueden comparar por igualdad). Una definición de '__lt__' que permite' x

+2

cuando no se puede ordenar el conjunto (por ejemplo, un conjunto de números complejos), su solución fallará de todos modos con un TypeError. Pero considere 'clase epsilon (float): def __lt __ (self, otro): return True if 0 ch3ka

-1

Si realmente se dirigen a una versión particular de pitón, a continuación, se puede ver en la fuente, y poner a prueba su comportamiento (pero probar así - tener en cuenta los factores de carga y similares).

Si quieren portabilidad, o si se encuentra set no funcione como es necesario, utilice un ordereddict (aquí está uno: http://code.activestate.com/recipes/576693/; hay un montón de otros, por lo que encontrar uno que te gusta el aspecto de), y adaptarlo como una conjunto.

Actualización: aquí hay un conjunto ordenado: http://packages.python.org/Brownie/api/datastructures.html#brownie.datastructures.OrderedSet

+0

Ordereddict está en stdlib en 2.7 y 3.1+ (http://docs.python.org/library/collections.html # collections.OrderedDict, http://docs.python.org/dev/library/collections.html#collections.OrderedDict) – miku

+0

@miku Dado que está implementado en C, no se puede adaptar de forma portátil, como se especifica en la misma oración usted está respondiendo – Marcin

Cuestiones relacionadas