56 votes

Set littéral donne un résultat différent de l'appel de la fonction set

Pourquoi la fonction set appelle-t-elle les doublons, mais pas l'analyse d'un littéral défini?

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

(Python 2.7.12. Peut-être la même cause que pour cette question similaire)

56voto

Martijn Pieters Points 271458

Jeux de test pour l'égalité, et jusqu'à ce qu'il y a de nouvelles versions de Python, l'ordre dans lequel ils le font peut varier en fonction de la forme de la main, les valeurs de l'ensemble en cours de construction, je vais montrer ci-dessous.

Depuis 0 == x est vrai et 0 == y est vrai, mais x == y est faux, le comportement ici, c'est vraiment pas défini, comme l'ensemble suppose qu' x == y doit être vrai si les deux premiers tests ont été aussi vrai.

Si vous inversez la liste transmise à l' set(), puis vous obtenez le même résultat que l'utilisation d'un littéral, parce que l'ordre des tests d'égalité des changements:

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

et de même pour inverser le sens littéral:

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

Ce qui se passe est que l'ensemble littérale charge les valeurs sur la pile, puis la pile de valeurs sont ajoutées à la nouvelle série de l'objet dans l'ordre inverse.

Tant que 0 est chargé en premier, les deux autres objets sont ensuite testés contre 0 déjà dans le jeu. Le moment où l'un de l'autre deux objets est chargé en premier, l'égalité test échoue, et vous obtenez deux objets ajoutés:

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

Que l'ensemble des littéraux ajouter des éléments dans le sens inverse est un bug présent dans toutes les versions de Python qui prennent en charge la syntaxe, tout le chemin jusqu'à ce que Python 2.7.12 et 3.5.2. Il a été récemment résolu, voir question 26020 (partie de 2.7.13, 3.5.3 et 3.6, aucun n'a encore été dévoilé). Si vous regardez 2.7.12, vous pouvez voir qu' BUILD_SET en ceval.c lit la pile de haut en bas:

# 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);
}

alors que le bytecode ajoute des éléments de la pile dans l'ordre inverse (en appuyant 0 sur la pile en premier):

>>> 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 solution est de lire les éléments de la pile dans l'ordre inverse; le Python 2.7.13 version utilise PEEK() au lieu de POP() (et un STACKADJ() supprimer les éléments de la pile par la suite):

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

L'égalité d'essais problème a la même cause que l'autre question; l' Decimal() classe est d'avoir quelques questions d'égalité avec complex ici, qui a été fixé en Python 3.2 ( Decimal() soutien des comparaisons complex et quelques autres types numériques, il n'était pas avant).

7voto

Mark Ransom Points 132545

Tout dépend de l'ordre dans lequel l'ensemble est construit, combiné au bogue que vous avez découvert avec votre autre question . Il semble que le littéral soit construit dans l'ordre inverse de la conversion à partir d'une liste.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X