Python 2 和 3 中的不确定集

Indeterministic sets in Python 2 and 3

Python 2

集合是无序值的集合。如果我通过集合文字构造一个集合,例如

s = {'a', 'b', 'c'}

然后打印它,我得到了一些乱序的元素。但是,似乎在 Python 2.7 中,上面的示例总是产生相同的顺序:

print(s)  # set(['a', 'c', 'b']) in Python 2.7

Python 2.7 是如何决定这个顺序的?甚至 'a''b''c' 的哈希值也不符合生成的顺序。

Python 3

在 Python 3.x 中(包括 3.6,其中 dict 键被排序)结果顺序似乎是随机的,尽管在给定的 Python 过程中总是相同的.也就是说,只要我不重新启动 Python 解释器,反复重新构建设置文字总是会导致相同的排序。

要检查多个 Python 进程的顺序,请考虑 bash 代码

(for _ in {1..50}; do python3 -c "s = {'a', 'b', 'c'}; print(s)"; done) | sort -u

这将(通常)显示 3 个元素的 6 种不同排列方式。将 python3 换成 python(2),我们只能看到顺序 ['a', 'c', 'b']。什么决定了 Python 3 中的顺序?

我看到对象的 hash 值在 Python 2 中是确定性的,而在 Python 3 中是随机的(尽管在 Python 过程中是常数)。我'我确定这是完整解释的关键。

编辑

正如 deceze 在他的评论中所写,我想知道 Python 是否明确地做了一些事情来实现这种随机化,或者它是否发生了 "for free".

Python 3(从 Python 3.3 开始)不同的原因是默认启用哈希随机化,您可以通过设置 PYTHONHASHSEED 环境来关闭它变量为固定值:

$ export PYTHONHASHSEED=0
$ (for _ in {1..50}; do python3  -c "s = {'a', 'b', 'c'}; print(s)"; done) | sort -u
{'a', 'b', 'c'}

同样,您可以使用 -R flag:

在 Python 2 中打开哈希随机化
$ (for _ in {1..50}; do python2 -R -c "s = {'a', 'b', 'c'}; print(s)"; done) | sort -u
set(['a', 'b', 'c'])
set(['a', 'c', 'b'])
set(['b', 'c', 'a'])
set(['c', 'b', 'a'])

请注意,您通常不想将其关闭,因为启用哈希随机化有助于防止某些拒绝服务攻击。