【问题标题】:Why are python sets "sorted" in ascending order? [duplicate]为什么python集合按升序“排序”? [复制]
【发布时间】:2022-01-21 17:07:25
【问题描述】:

让我们运行以下代码:

st = {3, 1, 2}
st
>>> {1, 2, 3}
st.pop()
>>> 1
st.pop()
>>> 2
st.pop()
>>> 3

虽然说集合是无序的,但这个集合的行为就好像它是按升序排序的。根据文档,应该返回“任意元素”的方法pop() 也以升序返回元素。这是什么原因?

【问题讨论】:

  • afaik 它是完全随机的,并且根据对象的哈希值
  • "unordered" 意味着它不能被依赖来被订购,而不是可以被依赖来not被订购。 “任意元素”的想法相同。
  • .pop 按照元素在集合中的存在顺序移除元素。 set.pop 的文档说“任意”,因为集合的基本顺序是未定义的。这是因为集合的数学(和 Python)定义只关心集合中的成员,而不关心集合中的顺序。集合内的排序基于哈希码,这是不可预测的。
  • 集合中的物品必须按一些的顺序进行存放(并取出),只是您不能依赖顺序。
  • second linked duplicate's accepted answer 它应该很有启发性。但我只想说,当人们说“集合是无序的”时,他们的意思是“你不能依赖任何特定的顺序”

标签: python python-3.x set python-3.9 unordered


【解决方案1】:

顺序与对象的哈希、集合的大小、数字的二进制表示、插入顺序和其他实现参数相关。这是完全任意的,不应依赖:

>>> st = {3, 1, 2,4,9,124124,124124124124,123,12,41,15,}
>>> st
{1, 2, 3, 4, 9, 41, 12, 15, 124124, 123, 124124124124}
>>> st.pop()
1
>>> st.pop()
2
>>> st.pop()
3
>>> st.pop()
4
>>> st.pop()
9
>>> st.pop()
41
>>> st.pop()
12
>>> {1, 41, 12}
{1, 12, 41}
>>> {1, 9, 41, 12}
{1, 12, 9, 41}  # Looks like 9 wants to go after 12.
>>> hash(9)
9
>>> hash(12)
12
>>> hash(41)
41
>>> {1, 2, 3, 4, 9, 41, 12}
{1, 2, 3, 4, 9, 12, 41}  # 12 before 41
>>> {1, 2, 3, 4, 9, 41, 12, 15}  # add 15 at the end
{1, 2, 3, 4, 9, 41, 12, 15}  # 12 after 41

【讨论】:

  • @MichaelSzczesny 我并不是说实际上是随机的。我只是说它完全依赖于实现。
【解决方案2】:

你永远不应该依赖集合的顺序。一旦您将多个事物放入集合中,就永远无法依赖元素之间的稳定顺序。这就是 Python 需要你遵守的契约。

实现细节可能会向您展示一些行为,这些行为可能会让您相信您有时可以预测集合的顺序。例如,正如您所发现的,如果您将一些低位的降序整数放入一个集合中并打印该集合,它们将按升序显示。这可能是因为一个整数的哈希值等于它自己。

hash(1) == 1
hash(2) == 2

(您可以制作一个 Python 程序来确认这一点)。

set 的一个实现细节是它在内部使用散列和桶存储策略——与 Python 的 dict 一样。这方面的一个实现细节是,根据散列码在内部如何分区,以松散的顺序遍历项目。因此对于 Python 的某些实现,例如在 CPython 中,你有时会看到整数的确定性排序顺序,因为整数的哈希与整数本身是如此紧密地绑定在一起(它们是相同的值),并且这与哈希遍历的实现细节相互作用。

set 的合同告诉您永远不要依赖这种行为来保证未来的安全性和跨 Python 解释器的兼容性。

另一个实现细节是.pop set 将(对于 Python 的某些实现)按照您看到元素的顺序返回集合中的元素。因此,如果您打印一个集合以查看其内容,然后从该集合中弹出所有元素,它们将按照您在打印该集合时看到的顺序弹出。

作为一个实现细节,只要集合的内容没有改变,集合的顺序是“稳定的”。你不应该依赖这个。允许弹出从集合中选择任意元素,但不是必需的。

【讨论】:

  • 它有更多的实现细节,小数字的散列只是我上一个例子中明显的一个小因素。
  • 是的,我看到 12 在 41 之后。但是数字 0 到 9 将彼此分组,至少在我拥有的 Python 实现中是这样。我对哈希策略的实施了解不多,无法知道为什么会发生这种情况,但同样,我们不应该依赖它。
猜你喜欢
  • 2018-11-20
  • 2012-07-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
相关资源
最近更新 更多