Python 为什么从不同初始化集构造的元组相等?
我预期会出现以下两个元组Python 为什么从不同初始化集构造的元组相等?,python,set,comparison,tuples,hashtable,Python,Set,Comparison,Tuples,Hashtable,我预期会出现以下两个元组 >>> x = tuple(set([1, "a", "b", "c", "z", "f"])) >>> y = tuple(set(["a", "b", "c", "z", "f", 1])) 比较不相等,但它们不相等: >>> x == y >>> True 为什么会这样?集合不是有序的,只由其成员身份定义 例如,set([1,2])==set([2,1]) 如果元组在每个位置的成员相等,则
>>> x = tuple(set([1, "a", "b", "c", "z", "f"]))
>>> y = tuple(set(["a", "b", "c", "z", "f", 1]))
比较不相等,但它们不相等:
>>> x == y
>>> True
为什么会这样?集合不是有序的,只由其成员身份定义 例如,
set([1,2])==set([2,1])
如果元组在每个位置的成员相等,则元组是相等的,但由于元组的集合是通过相等的迭代(以递增的顺序)创建的,因此元组最终也是相等的。因此您有两个列表,它们具有相同的内容,但顺序不同,您可以将它们转换为相等的集合,因为他们有相同的内容 当您将这些集合转换为元组时,它们将按照相同的顺序进行转换,因为它们是相同的集合,所以元组将是相同的
这在Python2.7中是正确的,但从3.3开始,当散列随机时,你将无法保证这一点,因为这两个集合虽然内容相同,但不一定以相同的顺序迭代。这里有两件事在起作用
tuple
构造函数将一个集合转换为一个tuple时,它实际上会在集合上迭代,并添加迭代返回的每个元素tuple(iterable) -> tuple initialized from iterable's items
调用元组(set([1,“a”,“b”,“c”,“z”,“f”]))
与调用元组([1,“a”,“b”,“c”,“z”,“f”])是相同的
价值观
[i for i in set([1, "a", "b", "c", "z", "f"])]
及
与它在同一集合上的迭代相同
编辑感谢@ZeroPiraeus(查看他的)。这不是保证。即使对于相同的集合,迭代的值也不总是相同的
元组构造函数不知道集合的构造顺序。乍一看,似乎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=元组(集合([1,“a”,“b”,“c”,“z”,“f”]))
y=元组(集合([“a”、“b”、“c”、“z”、“f”、1]))
打印(x==y)
。。。一千次:
{1..1000}中x的$
>做
>python3.3.py
>完成|排序| uniq-c
147错
853正确
这是因为,自Python3.3以来,字符串、字节和日期时间的散列值都是随机的,这是由于。根据散列是什么,可能会发生“冲突”,这意味着顺序项存储在基础数组中(因此迭代顺序)取决于插入顺序
以下是文档中的相关部分:
安全改进:
- 默认情况下,将打开散列随机化
-
编辑:由于评论中提到上述True
/False
比率表面上令人惊讶
集合,就像字典一样,被实现为哈希表——因此,如果发生冲突,表中项目的顺序(以及迭代的顺序)将取决于首先添加的项目(在本例中不同于x
和y
),以及用于哈希的种子(自3.3以来不同于Python调用)。由于碰撞在设计上是罕见的,而且这个问题中的例子都是小集合,所以这个问题并不像人们最初想象的那样经常出现
有关Python实现字典和集合的详细说明,请参见。为什么您认为它是错误的?这两个集合的内容是相同的!正如Zero Piraeus所说,哈希随机化使得字符串、字节和日期的情况变得不真实,因为每次实例化解释器时,hash(a_string)
都会发生变化。因此,如果两个set
s中的值相同,它们将以相同的顺序进行迭代,这能保证吗?@no,事实并非如此:请参见。您应该根据@ZeroPiraeus的答案更新您的答案-“和的值与它在同一集合上迭代时的值相同”并不总是正确的。@正如我所说的,自Python 3.3以来,行为已经改变。另请参见:“更改哈希值会影响dict、set和其他映射的迭代顺序。Python从未对此顺序做出过保证(通常在32位和64位构建之间有所不同)。”(我的重点是)哈希值可能仍然是完全随机的,但853/1000不是冲突率的度量,这是两个不同集合之间哈希顺序相同的度量。取决于散列的精确计算方式-散列(“a”)<散列(“b”)很可能是真的,即使随机性很高(通过多次调用散列(项))@TonySuffolk66不,这是冲突的度量-重要的是底层数组中的顺序,只有当两个项目的(截断的)散列相同时,x
和y
才会有所不同(因此插入的第二个项目必须根据其散列到达第一个选择之外的其他地方)。@Floris数字为5/32。请看:@TonySuffolk66这正是衡量碰撞率的标准。请看:我没有投反对票,但不管散列随机化如何,您的答案都是错误的:例如,在2.4版之后的每一条Python中(当set
成为内置时),tuple(set([0,8])!=tuple(set([8,0])
(这同样适用于Python 2.3中的set.set
)。因此,一个集合不做集合应该做的事情——即当内容相等时相等——非常奇怪。两个对象相等的事实并不一定意味着对它们进行相同操作的结果
>>> 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