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

时间:2015-08-23 08:58:29

标签: python python-3.x for-loop set unordered

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

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,则可能会获得相同的结果,如评论中所建议的那样。另外,如果您使用控制台进行复制它,确保每次运行代码时重新运行控制台。)

然而,当将上述代码放在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次而不更改。

为什么不改变?

2 个答案:

答案 0 :(得分:5)

@ReblochonMasque有一个正确的观点:set基于哈希表,如果运行之间计算的哈希值相同,则运行之间的顺序相同。但是,这种行为容易受到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__()。底部有一个注释(完全与您的情况有关):

  

注意默认情况下,__hash__()strbytes个对象的datetime值为" salted"具有不可预测的随机值。尽管它们在单个Python进程中保持不变,但在重复调用Python之间无法预测它们。

答案 1 :(得分:4)

你不应该期望一组的顺序会改变;集合在某种意义上是无序的,即订单不是不变的i / e,不能保证它不会改变。

实现是以哈希表(字典)的形式;只要没有钥匙碰撞,订单可能不会改变,但没有说明。也无法预测是否或何时会发生这种情况。

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