name font python hash set python-2.x python-internals

python - font - Establecer literal da un resultado diferente de la llamada a la función establecida



subplot python title (2)

¿Por qué la llamada a la función set borra los duplicados, pero el análisis de un literal set no?

>>> x = Decimal(''0'') >>> y = complex(0,0) >>> set([0, x, y]) {0} >>> {0, x, y} {Decimal(''0''), 0j}

(Python 2.7.12. Posiblemente la misma causa raíz que this pregunta similar)


Prueba de igualdad de conjuntos, y hasta que haya nuevas versiones de Python, el orden en que lo hacen puede diferir según la forma en que entregue los valores al conjunto que se está construyendo, como mostraré a continuación.

Dado que 0 == x es verdadero y 0 == y es verdadero, pero x == y es falso , el comportamiento aquí es realmente indefinido , ya que el conjunto supone que x == y debe ser verdadero si las dos primeras pruebas también son verdaderas .

Si invierte la lista pasada a set() , obtendrá el mismo resultado que usar un literal, porque el orden de las pruebas de igualdad cambia:

>>> set([y, x, 0]) set([0j, Decimal(''0'')])

y lo mismo para revertir el literal:

>>> {y, x, 0} set([0])

Lo que sucede es que el conjunto literal carga los valores en la pila y luego los valores de la pila se agregan al nuevo objeto establecido en orden inverso.

Siempre que se cargue 0 primero , los otros dos objetos se prueban contra 0 ya están en el conjunto. En el momento en que uno de los otros dos objetos se carga primero, la prueba de igualdad falla y se agregan dos objetos:

>>> {y, 0, x} set([Decimal(''0''), 0j]) >>> {x, 0, y} set([0j, Decimal(''0'')])

Ese conjunto de literales agrega elementos en reversa es un error presente en todas las versiones de Python que admiten la sintaxis, hasta Python 2.7.12 y 3.5.2. Se solucionó recientemente, consulte el problema 26020 (parte de 2.7.13, 3.5.3 y 3.6, ninguno de los cuales se ha publicado aún). Si observa 2.7.12, puede ver que BUILD_SET en ceval.c lee la pila de arriba hacia abajo:

# oparg is the number of elements to take from the stack to add for (; --oparg >= 0;) { w = POP(); if (err == 0) err = PySet_Add(x, w); Py_DECREF(w); }

mientras que el bytecode agrega elementos a la pila en orden inverso (presionando 0 en la pila primero):

>>> from dis import dis >>> dis(compile(''{0, x, y}'', '''', ''eval'')) 2 0 LOAD_CONST 1 (0) 3 LOAD_GLOBAL 0 (x) 6 LOAD_GLOBAL 1 (y) 9 BUILD_SET 3 12 RETURN_VALUE

La solución es leer los elementos de la pila en orden inverso; la versión Python 2.7.13 usa PEEK() lugar de POP() (y un STACKADJ() para eliminar los elementos de la pila luego):

for (i = oparg; i > 0; i--) { w = PEEK(i); if (err == 0) err = PySet_Add(x, w); Py_DECREF(w); } STACKADJ(-oparg);

El problema de la prueba de igualdad tiene la misma causa raíz que la otra pregunta; la clase Decimal() está teniendo algunos problemas de igualdad con el complex aquí, que se corrigió en Python 3.2 (al hacer que Decimal() admitiera comparaciones con complex y algunos otros tipos numéricos que no admitía antes ).


Todo depende del orden en que se construye el conjunto, combinado con el error que descubrió con su otra pregunta . Parece que el literal se construye en el orden opuesto a la conversión de una lista.

>>> {0, x, y} set([0j, Decimal(''0'')]) >>> {y, x, 0} set([0])