Python3.6中的集合是否像字典一样有序?

47

由于 Python 3.6 中 dict 实现的改变,它现在默认会保持顺序。那么现在 set 也能保持顺序吗?

我没有找到相关信息,但由于这两种数据结构在底层工作方式上非常相似,我认为可能是这样。

我知道并不是所有情况下都可以保证 dict 有序,但大部分情况下是有序的。正如 Python 文档中所述:

这种新实现中保留顺序的方面被认为是实现细节,不应该依赖它


@byxor 你不应该依赖于随机顺序,集合是任意排序的,但由于哈希而远非随机。 - Chris_Rands
1
如果您对为什么集合不是插入有序的感兴趣,请参阅为什么Python集合不保留插入顺序? - wim
2个回答

30

不,set仍然是无序的。

您可以通过显示应具有“良好定义的哈希顺序”1set来验证这一点,以确保我们不会意外地得到一个看起来有序但实际上并非如此的set

>>> a_set = {3,2,1}
>>> a_set
{1, 2, 3}
>>> list(a_set)
[1, 2, 3]
如果被排序,你可以期望这些例子的结果是{3, 2, 1}[3, 2, 1]
尽管字典实际上是有序的(同样的例子只是稍作修改):
>>> a_dict = {3: 3, 2: 2, 1:1}
>>> a_dict
{3: 3, 2: 2, 1: 1}
>>> list(a_dict)
[3, 2, 1]

1“良好定义的哈希顺序”:

对于满足0 <= integer < sys.hash_info.modulus的整数,其哈希值就是该数字本身。这意味着如果基于哈希(而不是插入时间)对集合进行排序,并且哈希值不冲突(这就是我使用小数字和仅相差一的数字的原因),那么顺序应该是确定性的,因为它们占据了集合内彼此相邻的插槽:

  • 要么从最小到最大
  • 或者从特定值到最高值,然后从最小值到特定值。如果集合中下一个(即相邻的)空插槽是第一个插槽,则会出现这种情况。

以下是后一种情况的示例:

>>> a_set = {6,7,8,9}
>>> a_set
{8, 9, 6, 7}

负数的 int 也会哈希到它们自己(除了 -1),尽管我不确定确切的下限是什么。 - Chris_Rands
1
@Chris_Rands 是的,但由于 -1-2 都会哈希到 -2,所以发生了冲突。 :) - MSeifert
1
是的,-1 的行为是这样的,因为它是 C 语言中的错误代码;我相信边界是(sys.maxsize // 4) - 1,至少这是 Martijn Pieters 之前告诉我的。 - Chris_Rands
知道这点很好。但是如果想在hash期间捕获错误,这是有意义的。我还发现了最大值。它是sys.hash_info.modulus。 :) - MSeifert

12

set在Python 3.6中没有顺序,即使作为CPython实现的细节也是如此。一个简单的示例说明了这一点:

>>> import string
>>> string.digits
'0123456789'
>>> set(string.digits)
{'7', '0', '2', '8', '6', '9', '1', '5', '4', '3'}

Python 3 文档 明确指出:

集合是一个无序的、不含重复元素的集合。


但是dict的文档也说:“最好将字典视为一组无序的键值对,其中要求键是唯一的(在一个字典中)。“(来源)。这只是语言规范,实现可能是有序的... - MSeifert
@MSeifert,“最好考虑”是我认为的关键措辞,对于“set”文档没有这样的警告。 - Chris_Rands
我认为那只是“花哨的东西”(或者包含在PyPy中已经有一个有序字典的时间内)。但是,就像我说的那样,那只是语言规范。这并不意味着实现可以按顺序实现它(例如使用桶或类似方法)。 - MSeifert
注释已经过时了,文档中该部分的“unordered”一词现已删除。 - wim
我认为Python 3.6文档中的措辞是相同的。请参阅https://docs.python.org/3.6/tutorial/datastructures.html#dictionaries。 - Chris_Rands
这是有道理的,因为字典排序直到3.7才被正式认可。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接