我正在使用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']
为什么?
答案 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
。