我正在使用 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']
为什么?
最佳答案
Python 字典是作为哈希表实现的。在大多数 Python 版本中(稍后详细介绍),迭代字典时获取键的顺序是表中值的任意顺序,这与它们的添加顺序关系不大(当发生哈希冲突时,插入的顺序可能会有点影响)。该顺序取决于实现。 Python 语言不提供任何关于顺序的保证,只是如果中间没有添加或删除键,则在字典的多次迭代中它将保持不变。
对于带有整数键的字典,哈希表不会做任何花哨的事情。整数对自身进行哈希处理(-1
除外),因此将相同的数字放入字典中,您将在哈希表中获得一致的顺序。
但是,对于具有 bytes
键的字典,由于哈希随机化,您会看到不同的行为。为了防止一种字典冲突攻击(其中用 Python 实现的 Web 应用程序可以通过向其发送具有数千个 key 的数据来进行攻击,这些 key 哈希为相同的值,从而导致大量冲突并且非常糟糕(O(N**2 )
)性能),Python 每次启动时都会选择一个随机种子,并使用它来随机化 Unicode 和字节字符串以及 datetime
类型的哈希函数。
您可以通过将环境变量 PYTHONHASHSEED
设置为 0
来禁用哈希随机化(或者您可以通过将其设置为最大 的任何正整数来选择自己的种子) >2**32-1
)。
值得注意的是,这种行为在 Python 3.6 中发生了变化。哈希随机化仍然发生,但字典的迭代顺序不再基于键的哈希值。虽然官方语言政策仍然是顺序是任意的,但 CPython 中 dict 的实现现在保留了其值添加的顺序。在使用常规 dict 时,您不应该依赖此行为,因为开发人员有可能(尽管目前看来不太可能)认为这是一个错误并再次更改实现。如果您想保证迭代按特定顺序发生,请使用collections.OrderedDict
类而不是普通的dict
。
关于python - 为什么 Python list() 并不总是具有相同的顺序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47008930/