PyYAML 文件高效管理



我正在编写一个Python程序,该程序维护一个联系人列表,每个联系人有3个字段:

  1. 名字
  2. 电话号码
  3. 电子邮件

联系人需要保存在YAML结构化文件中,该程序应该提供添加新联系人的功能。

我的代码是:

class contacts:
def add_contact(self,file,contact):
if not os.path.exists(file):
#Creating for the first time
temp = []
temp.append(contact)
with open(file, "w") as file_desc:
yaml.dump(temp, file_desc, default_flow_style=False)
file_desc.close()
else:
#Second onwards
with open(file, "r") as file_desc:
loaded = yaml.safe_load(file_desc)
loaded.append(contact)
with open(file, "w") as file_desc2:
yaml.dump(loaded, file_desc2, default_flow_style=False)
file_desc2.close()
file_desc.close()
if __name__ == "__main__":
data1 = {'name' :'Abcd', 'phone': 1234, 'email': 'abcd@gmail.com'}
data2 = {'name': 'efgh', 'phone': 5678, 'email': 'efgh@gmail.com'}
contact = contacts()
contact.add_contact("contacts.yaml", data1)
contact.add_contact("contacts.yaml",data2)

我认为这是一个低效的实现。如果我们有 100 万个联系人,并且我们想添加一个新联系人,这将首先读取所有联系人,将一个附加到列表中并再次写入所有 100 万个 + 1 个联系人。有没有办法只添加新联系人而无需再次写入整个文件。我想阅读很重要,因为我不想存储重复的联系人,这需要比较。 任何其他有效的办法也将不胜感激。

在长时间运行的程序/进程 P 中,确实不需要重新读取 数据。 有几点需要记住:

  1. 如果仅在其他程序中使用 YAML 文档,则在 P 已经停止,那么只需要在P退出时写出文件。 您可能希望使用atexit, 如果您没有一个出口点

  2. 如果其他程序可能在 P 运行时编辑/更新列表,则 请确保检查 YAML 文件的日期时间戳,并且 在添加新联系人之前重新读取文件。 如有必要,您可以 使用锁以确保一次只有一个程序,更新 文件。

  3. 如果其他程序需要最新的 YAML 文档,您可以 要么在每次更新时写出 YAML,要么可以使用一些 通知 P 需要编写 YAML 文档的机制。我 使用过两个SIGINT 处理和基于零MQ的通信来做到这一点。

如果您使用真实的数据库,以上很多内容都是为您完成的,并且对于 简单的记录表,它们都具有相同的字段,这可能是一个 更好的选择。然而,一旦事情变得更加复杂: 每条记录的不同字段,复杂和可能的递归数据,然后 许多(SQL(数据库成为一个额外的问题,而不是帮助 解决您尝试解决的问题。


ruamel.yaml.base(免责声明:我是该软件包的作者(确实 项目2(对于您来说,开箱即用,其他两个项目很容易 也实现了。唯一棘手的是YAMLBase通常期望新文件的根级别的映射/字典,因此 当文件尚不存在时,需要进行一些强制。

执行pip install ruamel.yaml.base后:

import os
import ruamel.yaml
from ruamel.yaml.base import YAMLBase
yaml_path = 'contacts.yaml'
class Contacts(YAMLBase):
def __init__(self, path=yaml_path, verbose=0):
self._create_ok = True  # so the file is auto created if it doesn't exists
super().__init__(path=path, verbose=verbose)
if not os.path.exists(yaml_path):
# this is necessary to force block style sequence at the top
self._data = ruamel.yaml.comments.CommentedSeq()
self._changed = True
def add_record(self, contact):
self.data.append(contact)
self._changed = True  # this signals that writing is necessary
def dump_file(self):
"""dump the contents of the file on disc"""
print('dumping: "{}"'.format(self._path))
with open(yaml_path) as fp:
print(fp.read(), end='')

data1 = {'name' :'Abcd', 'phone': 1234, 'email': 'abcd@gmail.com'}
data2 = {'name': 'efgh', 'phone': 5678, 'email': 'efgh@gmail.com'}
contacts = Contacts()
contacts.add_record(data1)
contacts.save()  # optional
contacts.dump_file()
# this is just for checking 
contacts.add_record(data2)
contacts.save()
contacts.dump_file()

这给了:

dumping: "contacts.yaml"
- name: Abcd
phone: 1234
email: abcd@gmail.com
dumping: "contacts.yaml"
- name: Abcd
phone: 1234
email: abcd@gmail.com
- name: efgh
phone: 5678
email: efgh@gmail.com

如果将verbose参数设置为1,您将获得一些信息 在标准输出上关于包装中发生的事情。

如果您有很多记录,那么您可能希望更改self.dataContactsself.fast_data,这将使用更快的基于 C 的加载 YAML 装载机,代价是无法保存(手动添加( 输入 YAML 中的注释等。(无论哪种情况,都使用"safe_load"(。

最新更新