Python 2和3中的不确定集

jmd*_*_dk 0 python set non-deterministic python-2.7 python-3.x

Python 2

集合是无序值的集合.如果我通过set literal构造一个集合,例如

s = {'a', 'b', 'c'}
Run Code Online (Sandbox Code Playgroud)

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

print(s)  # set(['a', 'c', 'b']) in Python 2.7
Run Code Online (Sandbox Code Playgroud)

Python 2.7如何决定这种排序?即使的哈希值'a','b''c'不是产生的顺序.

Python 3

在Python 3.x(包括dict按键排序的3.6 )中,生成的顺序似乎是随机的,但在给定的Python进程中总是相同的.也就是说,只要我不重启Python解释器,反复重建set literal就会导致相同的顺序.

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

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

这将(通常)显示3种元素可以排列的6种不同方式.python3python(2)切换,我们只看到排序['a', 'c', 'b'].是什么决定了Python 3的排序?

我发现hash在Python 2中,对象的值是确定性的,而在Python 3中是随机的(虽然在Python过程中是常量).我确信这是完整解释的关键.

编辑

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

Chr*_*nds 5

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'}
Run Code Online (Sandbox Code Playgroud)

同样,您可以使用-R标志在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'])
Run Code Online (Sandbox Code Playgroud)

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