问题描述
我正在编写一个Python程序,该程序维护一个联系人列表,每个联系人都有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万个联系人,而我们想添加一个新联系人,则它将首先读取所有联系人,将其追加到列表中,然后再次写入所有1百万+ 1个联系人。 有没有一种方法可以只添加新的联系人而不必再次写入整个文件。 我想阅读很重要,因为我不想存储重复的联系人,因此需要进行比较。 任何其他有效的方法也将被理解。
1楼
在长时间运行的程序/过程P中,确实不需要重新读取数据。 有几件事要牢记:
如果在P停止时仅在其他程序中使用YAML文档,则仅当P退出时才需要写出该文件。 如果您没有单个退出点,则可能要使用这样做
如果在P运行时其他程序可能会编辑/更新列表,则请确保在添加新联系人之前检查YAML文件的日期时间戳并重新读取该文件。 如有必要,您可以使用锁来确保一次仅更新一个程序。
如果其他程序需要具有最新的YAML文档,则可以在每次更新时写出YAML,或者可以使用某种机制来通知P需要编写YAML文档。 我同时使用了处理和基于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
,则将在stdout上获取有关程序包中发生的情况的一些信息。
如果您有很多记录,则可能需要将Contacts
self.data
更改为self.fast_data
,这将使用更快的基于C的加载器加载YAML,但代价是无法保留(手动添加)输入的YAML中的注释等。
(无论哪种情况,都使用“ safe_load”)。