有没有比 pickle 或常规 Python 文件更快的存储大字典的方法?

Is there a faster way to store a big dictionary, than pickle or regular Python file?

我想存储一个只包含以下格式数据的字典:

{
    "key1" : True,
    "key2" : True,
    .....
}

换句话说,这是一种快速检查密钥是否有效的方法。我可以通过在一个名为 bar.py 的文件中存储一个名为 foo 的字典来做到这一点,然后在我的其他模块中,我可以按如下方式导入它:

from bar import foo

或者,我可以将它保存在一个名为 bar.pickle 的 pickle 文件中,然后在文件顶部导入它,如下所示:

import pickle  
with open('bar.pickle', 'rb') as f:
    foo = pickle.load(f)

哪种方法最理想,更快

Python 文件

使用 python 文件可以很容易地缓存字典,因此如果您 "import" 多次使用它,只需解析一次。但是,python 语法很复杂,因此加载文件的解析器可能没有针对您要保存的数据的有限复杂性进行优化(除非您包含任意 Python 对象和代码).查看和编辑方便,使用方便,但不易传输。

编辑:澄清一下,原始 Python 文件很容易被人修改,但计算机很难编辑。如果您的代码编辑了数据,并且您希望将其反映在字典中,那么您就大错特错了:相反,请使用以下方法之一。

泡菜文件

如果您使用 pickle 文件,您要么在每次使用时重新加载文件,要么在第一次读取文件后需要一些管理代码来缓存文件。与任意 Python 代码一样,pickle 文件可能非常复杂,它们的加载程序可能未针对您的特定数据类型进行优化,因为与原始 python 文件一样,它们也可以存储大多数任意 Python 对象。但是,对于普通人来说,它们很难编辑和查看,而且如果您移动数据,您可能会遇到可移植性问题。它也只能由 Python 读取,并且您需要考虑使用 pickle 的安全注意事项,因为加载 pickle 文件可能存在风险并且应该只使用受信任的文件来完成。

JSON 文件

如果您存储的只是简单的对象(字典、列表、字符串、布尔值、数字),请考虑使用 JSON 文件格式。 Python 有一个内置的 json 模块,它与 pickle 一样易于使用,因此没有增加复杂性。这些文件易于存储、查看、编辑和压缩(如果需要),并且看起来几乎与 python 词典完全一样。它具有高度可移植性(现在大多数常见语言都支持 reading/writing JSON 文件),如果您需要提高文件加载速度,ujson 模块是一个更快的替代品标准 json 模块。由于 JSON 文件格式相当受限,我希望它的解析器和编写器比常规 Python 或 Pickle 解析器(尤其是使用 ujson)快很多。

要添加到@scnerd 的评论中,这里是 IPython 中针对不同负载情况的计时。

这里我们创建一个字典并将其写入3种格式:

import random
import json
import pickle

letters = 'abcdefghijklmnopqrstuvwxyz'
d = {''.join(random.choices(letters, k=6)): random.choice([True, False]) 
     for _ in range(100000)}

# write a python file
with open('mydict.py', 'w') as fp:
    fp.write('d = {\n')
    for k,v in d.items():
        fp.write(f"'{k}':{v},\n")
    fp.write('None:False}')

# write a pickle file
with open('mydict.pickle', 'wb') as fp:
    pickle.dump(d, fp)

# write a json file
with open('mydict.json', 'wb') as fp:
    json.dump(d, fp)

Python 文件:

# on first import the file will be cached.  
%%timeit -n1 -r1
from mydict import d

644 ms ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

# after creating the __pycache__ folder, import is MUCH faster
%%timeit
from mydict import d

1.37 µs ± 54.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

pickle 文件:

%%timeit
with open('mydict.pickle', 'rb') as fp:
    pickle.load(fp)

52.4 ms ± 1.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

json 文件:

%%timeit
with open('mydict.json', 'rb') as fp:
    json.load(fp)

81.3 ms ± 2.21 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

# here is the same test with ujson
import ujson

%%timeit
with open('mydict.json', 'rb') as fp:
    ujson.load(fp)

51.2 ms ± 304 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)