Подтвердить что ты не робот

Набор литералов дает отличный результат при вызове функции set

Почему вызов функции set уничтожает обманы, но разбор заданного литерала не выполняется?

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

(Python 2.7.12. Возможно, такая же первопричина, что и для this аналогичный вопрос)

4b9b3361

Ответ 1

Устанавливает проверку на равенство, и пока не появятся новые выпуски Python, порядок, в котором они выполняются, может различаться в зависимости от формы, которую вы передаете значения, к конструируемому набору, как я покажу ниже.

Так как 0 == x истинно, а 0 == y истинно, но x == y является ложным, поведение здесь действительно undefined, так как множество предполагает, что x == y должно быть истинным, если первые два теста были истинными тоже.

Если вы отмените список, переданный на set(), вы получите тот же результат, что и с использованием литерала, потому что изменяется порядок тестов равенства:

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

и то же самое для обращения к буквалу:

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

Что происходит, так как набор литералов загружает значения в стек, а затем значения стека добавляются в новый заданный объект в обратном порядке.

Пока сначала загружается 0, остальные два объекта затем тестируются на 0 уже в наборе. Когда сначала загружается один из двух других объектов, тест равенства терпит неудачу, и вы добавите два объекта:

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

То, что набор литералов добавляет элементы в обратном порядке, является ошибкой, присутствующей во всех версиях Python, поддерживающих синтаксис, вплоть до Python 2.7.12 и 3.5.2. Это было недавно исправлено, см. issue 26020 (часть 2.7.13, 3.5.3 и 3.6, ни одна из которых еще не выпущена). Если вы посмотрите на 2.7.12, вы увидите, что BUILD_SET в ceval.c читает стек сверху вниз:

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

в то время как байт-код добавляет элементы в стек в обратном порядке (сначала нажатие 0 в стеке):

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

Исправление состоит в том, чтобы прочитать элементы из стека в обратном порядке; версия Python 2.7.13 использует PEEK() вместо POP()STACKADJ() для удаления элементов из стека):

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

Проблема проверки равенства имеет ту же самую основную причину, что и другой вопрос; класс Decimal() имеет некоторые проблемы с равенством complex здесь, который был исправлен в Python 3.2 (путем Decimal() сравнения поддержки с complex и нескольких других числовых типов он не поддерживался до).

Ответ 2

Все до порядка, в котором построено множество, в сочетании с ошибкой, которую вы обнаружили с помощью вашего другого вопроса. Похоже, что литерал построен в обратном порядке для преобразования из списка.

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