泡菜还是 Json?

我需要保存到磁盘一个小 dict对象的键是类型 str和值是 ints 然后找回来。就像这样:

{'juanjo': 2, 'pedro':99, 'other': 333}

什么是最好的选择? 为什么? 用 picklesimplejson序列化它?

我正在使用 Python 2.6。

114515 次浏览

如果您没有任何互操作性要求(例如,您只需要将数据与 Python 一起使用) ,并且采用二进制格式就可以了,那么可以使用 泡菜,它可以提供非常快速的 Python 对象序列化。

如果希望实现互操作性,或者希望使用文本格式存储数据,那么可以使用 JSON (或者根据约束使用其他适当的格式)。

对于我的序列化,我更喜欢 JSON 而不是 pickle。Unpickle 可以运行任意代码,使用 pickle在程序之间传输数据或在会话之间存储数据是一个安全漏洞。JSON 不会引入安全漏洞,而且是标准化的,所以如果需要的话,可以通过不同语言的程序访问数据。

JSON 还是 pickle? JSON 还有 pickle 怎么样!

您可以使用 jsonpickle。它很容易使用,磁盘上的文件是可读的,因为它是 JSON。

参见 Jsonpickle 文档

您可能还会发现这很有趣,可以比较一些图表: http://kovshenin.com/archives/pickle-vs-json-which-is-faster/

就个人而言,我通常更喜欢 JSON,因为它的数据是 人类可读。当然,如果您需要序列化某些 JSON 不会采用的东西,那么使用 pickle 就可以了。

但是对于大多数数据存储,您不需要序列化任何奇怪的东西,而且 JSON 更容易,并且总是允许您在文本编辑器中打开它并自己检查数据。

速度很好,但是对于大多数数据集来说,这种差异可以忽略不计; Python 通常不会太快。

如果主要关心速度和空间,那么使用 cPickle,因为 cPickle 比 JSON 快。

如果您更关心互操作性、安全性和/或人类可读性,那么使用 JSON。


其他答案中引用的测试结果是在2010年录制的,2016年更新的 cPickle 协议二测试结果显示:

  • CPickle 3.8 x 加载速度
  • CPickle 1.5 x 更快的阅读速度
  • CPickle 编码稍微小一些

使用 这个要点自己复制这个代码,它基于其他答案中引用的 康斯坦丁的基准,但是使用 cPickle 和 protocol 2代替 pickle,使用 json 代替 simplejson (因为是 Json 比 simplejson 快) ,例如。

wget https://gist.github.com/jdimatteo/af317ef24ccf1b3fa91f4399902bb534/raw/03e8dbab11b5605bc572bc117c8ac34cfa959a70/pickle_vs_json.py
python pickle_vs_json.py

在一个像样的2015至强处理器上使用 python 2.7的结果:

Dir Entries Method  Time    Length


dump    10  JSON    0.017   1484510
load    10  JSON    0.375   -
dump    10  Pickle  0.011   1428790
load    10  Pickle  0.098   -
dump    20  JSON    0.036   2969020
load    20  JSON    1.498   -
dump    20  Pickle  0.022   2857580
load    20  Pickle  0.394   -
dump    50  JSON    0.079   7422550
load    50  JSON    9.485   -
dump    50  Pickle  0.055   7143950
load    50  Pickle  2.518   -
dump    100 JSON    0.165   14845100
load    100 JSON    37.730  -
dump    100 Pickle  0.107   14287900
load    100 Pickle  9.907   -

使用 pickle 协议3的 Python 3.4甚至更快。

我尝试了几种方法,发现使用 cPickle 将 dump 方法的 protocol 参数设置为: cPickle.dumps(obj, protocol=cPickle.HIGHEST_PROTOCOL)是最快的 dump 方法。

import msgpack
import json
import pickle
import timeit
import cPickle
import numpy as np


num_tests = 10


obj = np.random.normal(0.5, 1, [240, 320, 3])


command = 'pickle.dumps(obj)'
setup = 'from __main__ import pickle, obj'
result = timeit.timeit(command, setup=setup, number=num_tests)
print("pickle:  %f seconds" % result)


command = 'cPickle.dumps(obj)'
setup = 'from __main__ import cPickle, obj'
result = timeit.timeit(command, setup=setup, number=num_tests)
print("cPickle:   %f seconds" % result)




command = 'cPickle.dumps(obj, protocol=cPickle.HIGHEST_PROTOCOL)'
setup = 'from __main__ import cPickle, obj'
result = timeit.timeit(command, setup=setup, number=num_tests)
print("cPickle highest:   %f seconds" % result)


command = 'json.dumps(obj.tolist())'
setup = 'from __main__ import json, obj'
result = timeit.timeit(command, setup=setup, number=num_tests)
print("json:   %f seconds" % result)




command = 'msgpack.packb(obj.tolist())'
setup = 'from __main__ import msgpack, obj'
result = timeit.timeit(command, setup=setup, number=num_tests)
print("msgpack:   %f seconds" % result)

产出:

pickle         :   0.847938 seconds
cPickle        :   0.810384 seconds
cPickle highest:   0.004283 seconds
json           :   1.769215 seconds
msgpack        :   0.270886 seconds

大多数答案都很老旧,而且漏掉了一些信息。

对于声明 ”取消 pickle 可以运行任意代码”:
  1. 检查 https://docs.python.org/3/library/pickle.html#restricting-globals中的示例
import pickle
pickle.loads(b"cos\nsystem\n(S'echo hello world'\ntR.")
pickle.loads(b"cos\nsystem\n(S'pwd'\ntR.")

可以用 rm代替 pwd来删除文件。

  1. 检查 https://checkoway.net/musings/pickle/获得更复杂的“运行任意代码”模板。代码是用 python2.7编写的,但是我猜经过一些修改,也可以在 python3中工作。如果您让它在 python3中工作,请添加 python3版本我的答案。:)
关于“腌菜速度 VS json”部分:

首先,现在 python3中没有显式的 cpickle

对于这个从另一个答案中借用的测试代码,pickle总体上打败了 json:

import pickle
import json, random
from time import time
from hashlib import md5


test_runs = 100000


if __name__ == "__main__":
payload = {
"float": [(random.randrange(0, 99) + random.random()) for i in range(1000)],
"int": [random.randrange(0, 9999) for i in range(1000)],
"str": [md5(str(random.random()).encode('utf8')).hexdigest() for i in range(1000)]
}
modules = [json, pickle]


for payload_type in payload:
data = payload[payload_type]
for module in modules:
start = time()
if module.__name__ in ['pickle']:
for i in range(test_runs): serialized = module.dumps(data)
else:
for i in range(test_runs):
# print(i)
serialized = module.dumps(data)
w = time() - start
start = time()
for i in range(test_runs):
unserialized = module.loads(serialized)
r = time() - start
print("%s %s W %.3f R %.3f" % (module.__name__, payload_type, w, r))

结果:

tian@tian-B250M-Wind:~/playground/pickle_vs_json$ p3 pickle_test.py
json float W 41.775 R 26.738
pickle float W 1.272 R 2.286
json int W 5.142 R 4.974
pickle int W 0.589 R 1.352
json str W 10.379 R 4.626
pickle str W 3.062 R 3.294