为什么有时保持订单顺序?

Fer*_*dox 2 python for-loop set unordered python-3.x

运行此代码时,结果会按预期更改,因为集合是无序的:

my_set_1 = {'a','b','c',}
print([i for i in my_set_1])
Run Code Online (Sandbox Code Playgroud)

也就是说,多次运行会给出不同的列表,例如

['a', 'c', 'b']
['b', 'a', 'c']
['a', 'c', 'b']
['c', 'b', 'a']
Run Code Online (Sandbox Code Playgroud)

等等

(注意:您可能会获得相同的结果,如果您没有PYTHONHASHSEED=random,请按照注释中的建议.另外,如果您使用控制台进行复制,请确保Rerun每次运行代码时都使用控制台.)


但是,将上面的代码放在for循环中时,结果相当令人惊讶:

for i in range(10):
    my_set_1 = {'a','b','c',}
    print([i for i in my_set_1])
# Prints: 
# ['a', 'c', 'b']
# ['a', 'c', 'b']
# ['a', 'c', 'b']
# ....
Run Code Online (Sandbox Code Playgroud)

for循环的单次运行将打印相同的列表.重新运行for循环可以打印不同的列表(例如['c', 'b', 'a']),但仍然可以打印10次而不更改.

为什么不改变?

Yar*_*min 5

@ReblochonMasque有一个正确的观点:set基于哈希表,如果运行之间计算的哈希值相同,则运行之间的顺序相同.但是这种行为很容易受到攻击.

为了防止这些攻击PYTHONHASHSEED,引入了特殊变量.当它设置为random每次运行时,Python将为相同的项生成不同的哈希值.这就是为什么你得到不同的订单.

要检查这一点,您可以使用PYTHONHASHSEED设置为相同的数字来运行程序.在运行中订单将是相同的.

$ export PYTHONHASHSEED=random
$ python t.py
['a', 'b', 'c']
$ python t.py
['a', 'c', 'b']
$ python t.py
['c', 'b', 'a']
$ export PYTHONHASHSEED=4
$ python t.py
['a', 'b', 'c']
$ python t.py
['a', 'b', 'c']
$ python t.py
['a', 'b', 'c']
Run Code Online (Sandbox Code Playgroud)

如果你看看object.__hash__().底部有一个注释(完全与您的情况有关):

注意:默认情况下,该__hash__()str,bytes并且datetime对象是"咸"不可预测的随机值.虽然它们在单个Python进程中保持不变,但是在重复调用Python之间它们是不可预测的.