python - PyYAML文件高效管理

标签 python python-3.x yaml 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 处理和基于 ZeroMQ 的通信来做到这一点。

如果您使用真实的数据库,并且对于 简单的记录表,所有记录都具有相同的字段,这可能是一个 更好的选择。然而,一旦事情变得更加复杂: 每条记录的不同字段,复杂且可能的递归数据,然后 许多(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,您将获得一些信息 在标准输出上了解包中发生的情况。

如果您有大量记录,那么您可能需要更改 Contacts 中的 self.dataself.fast_data,然后这将使用更快的基于 C 的加载 YAML loader,代价是无法保存(手工添加) 输入 YAML 中的注释等。 (无论哪种情况,都会使用“safe_load”)。

关于python - PyYAML文件高效管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52352421/

相关文章:

python - 如何在 Anaconda python 发行版上安装 AWSCLI

python - 使用时间模块测试复杂性

azure - 从脚本返回对象变量 - Azure YAML 管道

python - 使用另一列的值获取某一列的值

python - 网页字数统计

python - 将数字 1 添加到集合中没有任何效果

python - Flake8Lint 错误 : AttributeError: 'NoneType' object has no attribute 'kind'

python - 尝试播种 YAML 数据时出现 "value must be an integer"

r - Blogdown 中的 YAML 当前日期

python - 在 python 中动态生成变量名