用Python永久存储字典的优雅方法是什么?

35

目前正在昂贵地解析一个文件,生成了一个包含约400个键值对的字典,很少更新。之前有一个函数,它将文件解析后以字典语法写入文本文件中(例如dict = {'Adam': 'Room 430', 'Bob': 'Room 404'}),然后将其复制并粘贴到另一个函数中,后者的唯一目的是返回已解析的字典。

因此,在每个需要使用该字典的文件中,我都会导入该函数,并将其分配给一个变量,该变量现在就是该字典。想知道是否有更优雅的方法来做到这一点,而不涉及明确复制和粘贴代码?使用数据库似乎是不必要的,而文本文件让我可以在将其添加到函数之前查看是否正确解析。但我乐意听取建议。


可能重复:https://dev59.com/ymw05IYBdhLWcg3wrDlR - Matthew Adams
序列化为 JSON,将 JSON 写入文件,读取文件,然后使用 json.loads() - favoretti
8个回答

67
为什么不将它转储到一个JSON文件中,然后在需要时从那里加载它呢?
import json

with open('my_dict.json', 'w') as f:
    json.dump(my_dict, f)

# elsewhere...

with open('my_dict.json') as f:
    my_dict = json.load(f)

从JSON加载数据相当有效。

另一个选择是使用pickle,但与JSON不同的是,它生成的文件不可读,因此您会失去来自旧方法的可视化验证。


仅为完整性而补充一下,JSON不支持将整数用作键。JSON5可能是一个可行的替代方案。 - tommy.carstensen

26

为什么要费心使用所有这些序列化方法?它已经以Python字典的形式写入文件(尽管名称不幸是“dict”)。将程序更改为使用更好的变量名(例如"data"或"catalog")将数据写出并保存为Python文件,例如data.py。然后,您可以在运行时直接导入数据,而无需进行任何笨拙的复制/粘贴或JSON / shelve等解析:

from data import catalog

2
我不明白为什么这不是更受欢迎的选项。每个人似乎都默认使用json,但这使得程序员更容易阅读/编写导入,并且至少在我的系统上运行速度更快。 - T.C. Proctor
1
就我所见,唯一的缺点是没有一种直接、Pythonic 的方式来编写这样的字典,如果你想在 Python 之外使用你的字典,它也不具备可移植性。你可以通过以下代码从 Python 字典创建文件:with open("data.py", "w") as f: f.write("catalog = " + str(catalog))但是,如果你尝试写入任何非序列化的内容,它不会抛出错误,这会影响我的感官体验。 - T.C. Proctor
1
我已经编写了一个库来实现这个功能:https://github.com/blackhc/laaos它还以追加方式写入日志,因此除了尚未刷新的数据外,很少有崩溃导致数据丢失的可能性。 - BlackHC

6

在许多情况下,JSON可能是正确的选择;但也可能有一种替代方案。看起来你的键和值总是字符串,是吗?你可以考虑使用 dbm/anydbm。它们是“数据库”,但几乎完全像字典一样运作。它们非常适合于廉价数据持久性。

>>> import anydbm
>>> dict_of_strings = anydbm.open('data', 'c')
>>> dict_of_strings['foo'] = 'bar'
>>> dict_of_strings.close()
>>> dict_of_strings = anydbm.open('data')
>>> dict_of_strings['foo']
'bar'

5
如果所有的键都是字符串,你可以使用shelve模块。
一个shelf是一个持久化的、类似于字典的对象。它与“dbm”数据库的区别在于,shelf中的值(而不是键!)可以是任何pickle模块可以处理的Python对象——包括大多数类实例、递归数据类型和包含许多共享子对象的对象。键是普通字符串。
如果需要使用其他语言的数据,则json是一个不错的选择。

我也读到一些东西,让我相信shelve文件不是跨平台兼容的,因为使用的底层数据库可能因一个平台而异(并且没有很好的控制方法)。 - martineau

3
如果存储效率很重要,使用Pickle或CPickle(以获得执行性能的提升)。正如Amber所指出的那样,您还可以通过Json进行转储/加载。它将是人类可读的,但需要更多的磁盘空间。

3
我建议您考虑使用shelve模块,因为您的数据结构是映射关系。 这是我对类似问题If I want to build a custom database, how could I?答案。在我的另一个回答中还有一些示例代码,推荐使用它来回答How to get a object database?的问题。
ActiveState拥有一个评价很高的PersistentDict配方,支持csv、json和pickle输出文件格式。它非常快速,因为这三种格式都是用C实现的(尽管该配方本身是纯Python),所以当打开文件时,它将整个文件读入内存可能是可以接受的。

1

JSON(或YAML,或其他)序列化可能更好,但如果您已经在Python语法中将字典写入文本文件,并带有变量名称绑定,则可以将其直接写入.py文件中。然后,该Python文件将可导入并直接使用。没有必要采用“返回字典的函数”方法,因为您可以直接在该文件中将其用作全局变量。例如:

# generated.py
please_dont_use_dict_as_a_variable_name = {'Adam': 'Room 430', 'Bob': 'Room 404'}

而不是:

# manually_copied.py
def get_dict():
    return {'Adam': 'Room 430', 'Bob': 'Room 404'}

唯一的区别在于,manually_copied.get_dict 每次都会给你一个全新的字典副本,而 generated.please_dont_use_dict_as_a_variable_name[1] 是一个共享对象。如果您在检索后在程序中修改字典,则这可能很重要,但是如果您需要独立地修改其中一个副本,则始终可以使用 copy.copycopy.deepcopy 创建一个新副本。

[1] dictliststrintmap等通常被视为不良的变量名称。原因是这些名称已经作为内置函数定义,并且被广泛使用。因此,如果你给某个东西起了这样的名字,至少会对阅读你的代码的人(包括你离开一段时间后)造成认知失调,因为他们必须记住“dict在这里并不意味着它通常的含义”。而且很可能在某个时候,你会遇到一个令人恼火的难以解决的错误报告,指出dict对象不可调用(或其他类似问题),因为某些代码正在尝试使用类型dict,但实际上却得到了你绑定到名称dict的字典对象。


0

在JSON方向上,还有一种叫做simpleJSON的东西。我第一次在Python中使用json库时遇到了问题,无法弄清楚它的工作原理。simpleJSON更容易使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接