为什么Python list()并不总是有相同的顺序?

时间:2017-10-30 06:02:30

标签: python

我正在使用Python 3.5及其文档 https://docs.python.org/3.5/library/stdtypes.html#sequence-types-list-tuple-range 表示:

list([iterable])

(...)

The constructor builds a list whose items are the same and in the same order as iterable’s items. 

好的,对于以下脚本:

#!/usr/bin/python3

import random

def rand6():
    return random.randrange(63) 


random.seed(0)
check_dict = {}

check_dict[rand6()] = 1
check_dict[rand6()] = 1
check_dict[rand6()] = 1

print(list(check_dict))

我总是得到

[24, 48, 54]

但是,如果我将功能更改为:

def rand6():
    return bytes([random.randrange(63)])

然后返回的订单并不总是相同的:

>./foobar.py
[b'\x18', b'6', b'0']
>./foobar.py
[b'6', b'0', b'\x18']

为什么?

1 个答案:

答案 0 :(得分:3)

Python字典作为哈希表实现。在大多数Python版本中(稍后会详细介绍),迭代字典时获取键的顺序是表中值的任意顺序,这与它们的添加顺序几乎没有关系(当发生哈希冲突时,插入的顺序可能有点重要)。此顺序取决于实现。 Python语言不提供任何关于订单的保证,除非在字典中的几次迭代中保持相同,如果没有在其间添加或删除键。

对于带有整数键的词典,哈希表并不做任何花哨的事情。整数散列为自己(-1除外),因此在dict中输入相同的数字,就会在散列表中得到一致的顺序。

但是对于带有bytes键的词典,由于哈希随机化,您会看到不同的行为。为了防止一种字典冲突攻击(在Python中实现的webapp可以通过向数据库发送数据,其中数千个键散列到相同的值导致大量冲突和非常糟糕的(O(N**2))性能)来进行DoSed, Python每次启动时都会选择一个随机种子,并使用它来随机化Unicode和字节字符串以及datetime类型的哈希函数。

您可以通过将环境变量PYTHONHASHSEED设置为0来禁用哈希随机化(或者您可以通过将其设置为任意为2**32-1的正整数来选择自己的种子。)< / p>

值得注意的是,Python 3.6中的这种行为已经发生了变化。散列随机化仍然发生,但字典的迭代顺序不再基于键的散列值。虽然官方语言政策仍然是订单是任意的,但CPython中dict的实现现在保留了其值的添加顺序。在使用常规dict时,您不应该依赖此行为,因为它可能(尽管此时似乎不太可能)开发人员会认为这是一个错误并更改实现再次。如果您想保证迭代按特定顺序发生,请使用collections.OrderedDict类而不是普通dict