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

我期待以下两个元组

>>> 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 ,至less在Python> = 3.3的时候。 testing以下代码:

 # 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开始,string,字节和date时间的散列值随着安全性修正而被随机化。 根据散列是什么,可能会发生“冲突”,这意味着订单项存储在底层数组中(因此迭代顺序)取决于插入顺序。

以下是文档中的相关内容:

安全改进:

  • 哈希随机化是默认打开的。

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

编辑 :由于它在评论中提到,上面的True / False比例表面上令人惊讶…

像词典一样,集合被实现为哈希表 – 所以如果发生碰撞,表中项目的顺序(以及迭代次序)将取决于哪个项目被首先添加(在这种情况下xy不同)以及用于散列的种子(从3.3开始,不同的Python调用)。 由于碰撞在devise上很less见,而且这个问题中的例子是小的,所以这个问题并不像人们最初想象的那么频繁。

有关Python对词典和集合的实现的详细解释,请参阅The Mighty Dictionary 。

这里有两件事。

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

  2. 当你通过tuple构造函数把一个集合转换成一个元tuple它实际上遍历了这个集合,并添加了迭代返回的每个元素。

元组的构造器语法是

 tuple(iterable) -> tuple initialized from iterable's 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])

如果它们在每个位置上的成员是相等的,那么元组是相等的,但是由于元组是从相同迭代(按递增顺序)迭代创build的,所以元组最终也是相等的。

所以你有两个列表 – 它们具有相同的内容,但是按不同的顺序,将它们转换成集合 – 这将是平等的,因为它们具有相同的内容。

当你将这些集合转换为元组时,它们将按照相同的顺序进行转换,因为它们是相同的集合,所以元组将相同。

这在Python2.7中是正确的 – 但是从3.3开始,当散列是随机的时候,你将无法保证这一点 – 因为这两个集合虽然在内容上是相同的,但是在相同的顺序中不会重复。