为什么有时会保持既定秩序?

Why is set order maintained sometimes?

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

my_set_1 = {'a','b','c',}
print([i for i in my_set_1])

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

['a', 'c', 'b']
['b', 'a', 'c']
['a', 'c', 'b']
['c', 'b', 'a']

等等

(注意: 如果您没有 PYTHONHASHSEED=random,您可能会得到相同的结果,如评论中所建议的那样。另外,如果您是使用控制台复制它,确保每次 运行 代码时 Re运行 控制台。)


然而,当将上述代码放在 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']
# ....

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

为什么没有改变?

你不要指望集合的顺序会改变;从某种意义上说,集合是无序的,因为顺序不是不变的 i/e 不能保证它不会改变。

以hash的形式实现table(字典);只要没有按键冲突,顺序就可能不会改变,但没有人知道。也无法预测是否会发生或何时发生。

从您的实验中得出结论时要小心:您得到的结果无法预测,将取决于您 运行 时系统的状态。它们也不会跨平台、python 版本等...

@ReblochonMasque 有一个正确的观点:set 基于散列 table,如果 运行 之间计算的散列相同,则 运行 之间的顺序相同]s。然而,这种行为很容易受到 attacks 的影响。

为了防止这些攻击,引入了特殊变量 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']

如果你看object.__hash__()。底部有一条注释(正是关于您的情况):

Note By default, the __hash__() values of str, bytes and datetime objects are "salted" with an unpredictable random value. Although they remain constant within an individual Python process, they are not predictable between repeated invocations of Python.