Я ожидал следующие два набора:
>>> x = tuple(set([1, "a", "b", "c", "z", "f"]))
>>> y = tuple(set(["a", "b", "c", "z", "f", 1]))
сравнить неравные, но они этого не делают:
>>> x == y
>>> True
Почему это?
Я ожидал следующие два набора:
>>> x = tuple(set([1, "a", "b", "c", "z", "f"]))
>>> y = tuple(set(["a", "b", "c", "z", "f", 1]))
сравнить неравные, но они этого не делают:
>>> x == y
>>> True
Почему это?
На первый взгляд кажется, что x
должен всегда равняться y
, потому что два набора, построенные из одних и тех же элементов, всегда равны:
>>> x = set([1, "a", "b", "c", "z", "f"])
>>> y = set(["a", "b", "c", "z", "f", 1])
>>> x
{1, 'z', 'a', 'b', 'c', 'f'}
>>> y
{1, 'z', 'a', 'b', 'c', 'f'}
>>> x == y
True
Однако, не всегда бывает, что кортежи (или другие упорядоченные коллекции), построенные из двух равных множеств, равны.
Фактически, результат вашего сравнения иногда True
, а иногда False
, по крайней мере, в Python >= 3.3. Тестирование следующего кода:
# compare.py
x = tuple(set([1, "a", "b", "c", "z", "f"]))
y = tuple(set(["a", "b", "c", "z", "f", 1]))
print(x == y)
... тысячу раз:
$ for x in {1..1000}
> do
> python3.3 compare.py
> done | sort | uniq -c
147 False
853 True
Это связано с тем, что с Python 3.3 хеш-значения строк, байтов и datetime рандомизированы в результате исправления безопасности. В зависимости от того, что такое хеши, может произойти "столкновение", что будет означать, что элементы заказа хранятся в базовом массиве (и, следовательно, порядок итерации), зависят от порядка вставки.
Здесь соответствующий бит из документов:
Улучшения безопасности:
- Рандомизация хеширования включена по умолчанию.
EDIT: поскольку в комментариях упоминалось, что отношение True
/False
выше поверхностно удивительно...
Установки, такие как словари, реализуются как хеш-таблицы - поэтому, если есть столкновение, порядок элементов в таблице (и, следовательно, порядок итераций) будет зависеть как от того, какой элемент был добавлен первым (разные в x
и y
в этом случае) и семя, используемое для хэширования (по-разному между вызовами Python с 3.3). Так как столкновения редки по дизайну, а примеры в этом вопросе - это небольшие множества, проблема возникает не так часто, как можно было предположить изначально.
Подробное объяснение реализации словарей и наборов Python см. в Могущественном словаре.
Здесь есть две вещи.
Установки неупорядочены. set([1, "a", "b", "c", "z", "f"])) == set(["a", "b", "c", "z", "f", 1])
При преобразовании набора в кортеж через конструктор tuple
он по существу выполняет итерацию по набору и добавляет каждый элемент, возвращаемый итерацией.
Синтаксис конструктора для кортежей
tuple(iterable) -> tuple initialized from iterable items
Вызов tuple(set([1, "a", "b", "c", "z", "f"]))
совпадает с вызовом tuple([i for i in set([1, "a", "b", "c", "z", "f"])])
Значения для
[i for i in set([1, "a", "b", "c", "z", "f"])]
и
[i for i in set(["a", "b", "c", "z", "f", 1])]
- те же, что и итерации по одному и тому же набору.
ИЗМЕНИТЬ благодаря @ZeroPiraeus (проверьте его ответ). Это не гарантируется. Значение итерации не всегда будет одинаковым даже для одного и того же набора.
Конструктор кортежа не знает порядка, в котором построено множество.
Наборы не упорядочены и определяются только их принадлежностью.
Например, set([1, 2]) == set([2, 1])
Кортежи равны, если их члены в каждой позиции равны, но так как коллекции, которые корни были созданы из итерации одинаково (в порядке возрастания), кортежи также оказываются равными.
поэтому у вас есть два списка, которые имеют один и тот же контент, но в разных порядках, вы преобразуете их в множества, которые будут равны, так как они имеют одинаковый контент.
Когда вы конвертируете эти множества в кортежи, они будут преобразованы в том же порядке, что и тот же набор, поэтому кортежи будут одинаковыми.
Это верно в Python2.7, но начиная с 3.3, когда хеши рандомизированы, вы не сможете этого гарантировать - поскольку два набора, хотя они равны по содержанию, не обязательно должны повторяться в том же порядке.