python3.5 中字典中存储的对象的顺序在解释器的不同执行过程中会发生变化,但对于同一个解释器实例来说,它似乎保持不变。
$ python3 <(printf 'print({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})') {'b': 2, 'a': 1} {'b': 2, 'a': 1} {'b': 2, 'a': 1} {'b': 2, 'a': 1} $ python3 <(printf 'print({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})') {'a': 1, 'b': 2} {'a': 1, 'b': 2} {'a': 1, 'b': 2} {'a': 1, 'b': 2}
我一直以为顺序是基于密钥的哈希值。为什么在不同的 Python 执行过程中顺序会有所不同?
这是因为从 Python 3.3 开始,字典中键的哈希值由 随机化哈希种子(Hash Randomization Seed)影响,每次启动 Python 解释器时,这个种子都会重新生成。这会导致在不同的解释器执行过程中,即使是相同的字典定义,其键的顺序可能也会发生变化。
在单个 Python 进程中,随机哈希种子在解释器启动时固定。因此,字典中键的哈希值和插入顺序是确定的,这会使同一进程内同样的字典在不同位置出现时顺序保持一致。你运行的以下代码证明了这一点:
$ python3 <(printf 'print({"a": 1, "b": 2})\nprint({"a": 1, "b": 2})\n') {'b': 2, 'a': 1} {'b': 2, 'a': 1}
在同一进程中,由于使用相同的哈希种子,所有字典的键顺序是一致的。
每次启动 Python 解释器时,都会生成一个新的随机种子,导致键的哈希值不同,因此字典的顺序可能变化。例如:
$ python3 <(printf 'print({"a": 1, "b": 2})') {'b': 2, 'a': 1} $ python3 <(printf 'print({"a": 1, "b": 2})') {'a': 1, 'b': 2}
在每次新启动的 Python 进程中,种子随机化导致键的哈希值发生变化,从而引起顺序不同。
Python 字典底层使用哈希表(hash table)实现。键的哈希值决定它在表中的存储位置。以下是流程:
随机化种子通过影响哈希值改变了插入位置。
你可以通过以下代码显示哈希值,观察其在不同执行中的变化:
print(hash("a")) # 查看键 "a" 的哈希值 print(hash("b")) # 查看键 "b" 的哈希值
在两次不同的执行中,结果可能不同:
运行 1:
764128340 1829473081
运行 2:
-654892031 1298751043
哈希值的不同直接导致字典键的顺序变化。
你可以通过设置环境变量来禁用哈希随机化:
PYTHONHASHSEED=0 python3 script.py
这将强制所有哈希值在每次执行时都一致,从而使字典键的顺序固定。
PYTHONHASHSEED