为什么由不同初始化集合构造的元组相等?

Ash*_*and 38 python comparison tuples hashtable set

我期待以下两个元组

>>> x = tuple(set([1, "a", "b", "c", "z", "f"]))
>>> y = tuple(set(["a", "b", "c", "z", "f", 1]))
Run Code Online (Sandbox Code Playgroud)

比较不平等,但他们不:

>>> x == y
>>> True
Run Code Online (Sandbox Code Playgroud)

这是为什么?

Zer*_*eus 68

乍一看,似乎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
Run Code Online (Sandbox Code Playgroud)

但是,从两个相等集合构造的元组(或其他有序集合)并不总是相同的情况.

事实上,比较的结果有时候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)
Run Code Online (Sandbox Code Playgroud)

......一千次:

$ for x in {1..1000}
> do
>   python3.3 compare.py
> done | sort | uniq -c
147 False
853 True
Run Code Online (Sandbox Code Playgroud)

这是因为,因为Python 3.3,的字符串,字节,日期时间的哈希值被随机化,为的结果安全修补程序.根据哈希的内容,可能会发生"冲突",这意味着订单项存储在基础数组中(因此迭代顺序)取决于插入顺序.

这是文档中的相关位:

安全改进:

  • 默认情况下启用哈希随机化.

- https://docs.python.org/3/whatsnew/3.3.html

编辑:因为在评论中提到,上述True/ False比率表面上令人惊讶......

集,如字典,实现为哈希表-所以,如果有一个碰撞,项目在表中的顺序(因此迭代的顺序)将既取决于这是第一次增加(不同的项目x,并y在这种情况下)和用于散列的种子(自3.3以来不同的Python调用).由于碰撞在设计上很少见,而且这个问题中的例子都很小,所以问题并不像人们最初想象的那样经常出现.

有关Python的字典和集合实现的详细说明,请参阅The Mighty Dictionary.

  • @stalk正如我所说,**自Python 3.3**以来,行为已发生变化.另请参阅[`object .__ hash _`](https://docs.python.org/3/reference/datamodel.html#object.__hash__):"更改哈希值会影响dicts,sets和其他映射的迭代顺序.**Python从未对此排序提供保证**(通常在32位和64位版本之间有所不同)." (我的重点) (3认同)
  • 这很奇怪很多次都是如此. (3认同)
  • @Floris这个数字是5/32.请参阅:http://stackoverflow.com/questions/26136894/why-does-tupleset1-abczf-tupleseta-bczf-1/26136895#26136895 (3认同)
  • @TonySuffolk66这正是碰撞率的衡量标准.请参阅:http://stackoverflow.com/questions/26136894/why-does-tupleset1-abczf-tupleseta-bczf-1/26136895#26136895 (2认同)

srj*_*srj 13

这里有两件事情在玩.

  1. 集合是无序的. set([1, "a", "b", "c", "z", "f"])) == set(["a", "b", "c", "z", "f", 1])

  2. 当您通过tuple构造函数将集合转换为元组时,它基本上迭代集合并添加迭代返回的每个元素.

元组的构造函数语法是

tuple(iterable) -> tuple initialized from iterable's items
Run Code Online (Sandbox Code Playgroud)

呼叫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"])]
Run Code Online (Sandbox Code Playgroud)

[i for i in set(["a", "b", "c", "z", "f", 1])]
Run Code Online (Sandbox Code Playgroud)

与它在同一组上迭代相同.

编辑感谢@ZeroPiraeus(查看他的回答 ).这不保证.即使对于同一组,迭代的值也不总是相同的.

元组构造函数不知道构造集的顺序.

  • @stalk**没有**,它不是:见[我的回答](http://stackoverflow.com/a/26116307/1014938). (3认同)
  • 您应该根据@ ZeroPiraeus的答案更新您的答案 - "_和_的值与它在同一组上的迭代相同"并不总是如此. (2认同)

Ama*_*dan 6

集合不是有序的,仅由其成员资格定义.

例如, set([1, 2]) == set([2, 1])

如果每个位置的成员相等,则元组是相等的,但是由于集合的元组是从迭代中平均创建的(按递增顺序),元组最终也是相等的.


Ton*_* 66 5

所以你有两个列表 - 它们具有相同的内容但是在不同的顺序中,你将它们转换成集合 - 它们将是相同的,因为它们具有相同的内容.

当您将这些集转换为元组时,它们将以相同的顺序转换,因为它们是相同的集合,因此元组将是相同的.

在Python2.7中也是如此 - 但是从3.3开始,当哈希值随机化时,你将无法保证这一点 - 因为两个集合虽然内容相同,但不会以相同的顺序迭代.