pickle序列化顺序之谜

1 投票
1 回答
5130 浏览
提问于 2025-04-18 02:44

更新 2017年6月8日

虽然已经过去了3年,我的拉取请求仍然在等待处理,作为一种临时解决方案来强制输出顺序。Stream-Framework可能会重新考虑使用内容作为通知的关键。GitHub上的问题 #153提到了这一点。

问题

请看下面的示例:

import pickle
x = {'order_number': 'X', 'deal_url': 'J'}

pickle.dumps(x)
pickle.dumps(pickle.loads(pickle.dumps(x)))
pickle.dumps(pickle.loads(pickle.dumps(pickle.loads(pickle.dumps(x)))))

结果:

(dp0\nS'deal_url'\np1\nS'J'\np2\nsS'order_number'\np3\nS'X'\np4\ns.
(dp0\nS'order_number'\np1\nS'X'\np2\nsS'deal_url'\np3\nS'J'\np4\ns.
(dp0\nS'deal_url'\np1\nS'J'\np2\nsS'order_number'\np3\nS'X'\np4\ns.

很明显,每次输出的序列化结果都不一样。当我从任何键中删除一个字符时,这种情况就不会发生。我发现这是因为Stream-Framework使用了经过序列化的输出作为存储通知的键。在我们更好地理解这里发生了什么之前,我会提交拉取请求。我找到了解决这个问题的两种方法:

A - 在排序后转换为字典(是的,这在某种程度上提供了预期的副作用)

import operator
sorted_x = dict(sorted(x.iteritems(), key=operator.itemgetter(1)))

B - 移除下划线(但不确定这是否总是有效)

那么,是什么导致了字典排序时的这个神秘现象呢?

证明调用排序对字典进行处理可以产生相同结果的输出:

import operator
x = dict(sorted(x.iteritems(), key=operator.itemgetter(1)))

pickle.dumps(x)
"(dp0\nS'order_number'\np1\nS'X'\np2\nsS'deal_url'\np3\nS'J'\np4\ns."

x = pickle.loads(pickle.dumps(x))
x = dict(sorted(x.iteritems(), key=operator.itemgetter(1)))

pickle.dumps(x)
"(dp0\nS'order_number'\np1\nS'X'\np2\nsS'deal_url'\np3\nS'J'\np4\ns."

1 个回答

4

字典是一种无序的数据结构。这意味着里面的顺序是随意的,pickle(一个用于保存和加载Python对象的工具)会按照它们原本的样子来存储这些数据。如果你想要一个有序的字典,可以使用collections.OrderedDict

当你在解释器中玩的时候,看到的任何顺序其实只是解释器在配合你而已。

根据dict的文档:

最好把字典想象成一个无序的键值对集合,要求每个键在一个字典中是唯一的。

记住,函数dict.keys()dict.values()dict.items()返回的值也是无序的。

撰写回答