为什么从不同初始化集构造的元组是相等的?
我期待以下两个元组
>>> 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时间的散列值随着安全性修正而被随机化。 根据散列是什么,可能会发生“冲突”,这意味着订单项存储在底层数组中(因此迭代顺序)取决于插入顺序。
以下是文档中的相关内容:
安全改进:
- 哈希随机化是默认打开的。
编辑 :由于它在评论中提到,上面的True
/ False
比例表面上令人惊讶…
像词典一样,集合被实现为哈希表 – 所以如果发生碰撞,表中项目的顺序(以及迭代次序)将取决于哪个项目被首先添加(在这种情况下x
和y
不同)以及用于散列的种子(从3.3开始,不同的Python调用)。 由于碰撞在devise上很less见,而且这个问题中的例子是小的,所以这个问题并不像人们最初想象的那么频繁。
有关Python对词典和集合的实现的详细解释,请参阅The Mighty Dictionary 。
这里有两件事。
-
集合是无序的。
set([1, "a", "b", "c", "z", "f"])) == set(["a", "b", "c", "z", "f", 1])
-
当你通过
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开始,当散列是随机的时候,你将无法保证这一点 – 因为这两个集合虽然在内容上是相同的,但是在相同的顺序中不会重复。