当前位置: 首页 > news >正文

Python Cookbook-7.7 通过 shelve 修改对象

任务

你正在使用标准库模块shelve。你用shelve处理过的一些值是易变的对象(mutableobjects),而且你需要修改这些对象。

解决方案

shelve 模块提供了一种持久的字典——在强大的关系型数据库和简洁的 marshal、pickledbm 以及类似的文件格式之间,它有着重要的地位。然而,在使用shelve的时候有一些很典型的陷阱需要注意。先看看下面的交互式 Python 会话:

>>> import shelve
>>> #创建一个简单的例子shelf
>>> she = shelve.open('try.she', 'c')
>>>> for c in 'spam':she[e] = {c:23}
...
>>> for c in she.keys():print c, she[c]
...
p{'p':23}
s{'s':23}
a{'a':23}
m{'m':23}
>>> she.close()

这样我们就创建了 shelve 文件,向其中添加了数据,之后又关闭此文件,到现在为止一切正常。现在我们需要再次打开该文件并处理其中的数据:

>>> she = shelve.open('try.she', 'c')
>>> she ['p']
{'p':23}
>>> she['p']['p'] = 42
>>> she['p']
{'p':23}

这是怎么回事?我们明明把这个值设为 42,怎么 shelve 好像根本不理会我们的设定?问题的关键在于我们处理的其实只是 shelve 给我们的一个临时对象而已,而不是“真家伙”。当我们以默认选项打开 shelve 时,就像上面做法一样,它并不会跟踪对临时对象的修改。一个可行的方案是我们给临时对象绑定一个名字,然后完成我们的修改,之后再把改过的对象赋值给shelve 的子项:

>>> a = she['p']
>>> a['p'] = 42
>>> she['p'] = a
>>> she ['p']
{'P':42}
>>> she.close()

我们可以验证这个修改是否持久:

>>> she = shelve.open('try.she','c')
>>> for c in she.keys():print c,she[c]
...
p{'p':42}
s{'s':23}
a{'a':23}
m{'m':23}

一个更简单的方法是将 writeback选项设置为True,然后打开 shelve 对象:

>>> she = shelve.open('try.she','c',writeback=True)

打开 writeback 选项后,shelve 会跟踪所有从文件生成的对象,并在关闭之前将所有项回写到文件,这是因为在这个过程中它们可能被修改过。虽然代码简化了不少,但代价是高昂的,尤其是内存的消耗增加了很多。当我们需要从一个用 writeback=True 选项打开的 shelve 对象中读取很多对象时,即使我们只修改了这些对象中的少数几个,shelve仍然会把所有对象放入内存,这是因为它事先也不知道哪个对象会被修改。而前一个方法,我们主动担起责任来通知 shelve 发生的变化(通过将修改过的对象赋值回去),需要多花工夫,但是收获也不小,因为它的效率很高。

讨论

Python 标准库模块 shelve 在很多应用场合下都是很方便的工具,但它却隐藏了一个令人生厌的陷阱,虽然在 Python 的在线文档中对此有很详细的说明,但仍然很容易被忽略。假设你正在用 shelve 处理一些易变的对象,如字典或列表。很自然的,你可能会希望更改其中的一些对象,比如调用一些修改的方法(列表的 append、字典的 update等)或给对象的子项或属性赋予一个新值。不过,当你在做这些事的时候,变化并不会发生在 shelve 对象中。这是因为我们更改的不过是 shelve 对象通过__getitem__方法提供的一个临时对象而已,而且 shelve 对象在默认情况下并不会跟踪临时对象的变化,事实上,在把临时对象返回给我们之后,它就对临时对象完全不闻不问,不关心了。

如前面代码所示,一个方法是给临时对象绑定一个名字,通过这个名字完成对临时对象的所有操作,然后将更改过的新对象赋值给 shelve 对象的子项。当你给 shelve 对象的子项赋值时,shelve对象的__setitem__方法会被调用,它会以适当的方式更新 shelve对象,这样变化就被保存下来了。

另一个可选的方法是,可以在打开shelve 对象的时候增加 writeback=True 标志,之后 shelve 会跟踪每一个它给你的对象,并最终将它们存回磁盘。这个方法会节省一些琐碎的代码,更加省心,但是你要当心:如果你从shelve对象读取很多子项,但却只修改其中的少数几项,writeback方式的开销会很大,尤其是内存消耗。当你用writeback=True选项打开shelve 时,它会把所有它给你的子项放入内存,并在最后保存,这是因为它没有什么可靠的办法来判断你要改哪些项,而且也不知道当你关闭shelve 对象时究竟哪些项是被改过的。除非你要修改每一个读取的项(或者shelve 对象的大小和你的计算机内存总数比起来微不足道,可以忽略),否则我推荐前一种方法:给你从 shelve对象获得的需要修改的子项绑定一个名字,改完之后将每个子项赋值回 shelve 对象。

相关文章:

  • WebUI性能优化大师课:从毫秒到微秒的极致之旅
  • 蓝桥杯FPGA赛道第二次模拟题代码
  • 【Pandas】pandas DataFrame ewm
  • 复盘20250508
  • 坐标系与坐标系数转换
  • zookeeper实现分布式获取全局唯一自增ID的案例。
  • BFS算法的学习
  • Android平台FFmpeg视频解码全流程指南
  • 跨平台移动开发框架React Native和Flutter性能对比
  • GuPPy-v1.2.0安装与使用-生信工具52
  • 数字孪生医疗:构建患者特异性数字孪生体路径探析
  • JVM运行时数据区域(Run-Time Data Areas)的解析
  • 关于 wordpress 统计访问量初始数值错误的解决方法
  • Qt获取CPU使用率及内存占用大小
  • typecho中的Widget设计文档
  • 17.thinkphp的分页功能
  • 广州AI数字人:从“虚拟”走向“现实”的变革力量
  • 软件工程(五):设计模式
  • 体绘制中的传输函数(transfer func)介绍
  • 网站公安备案流程及审核时间
  • 人民日报整版调查:中小学春秋假,如何放得好推得开?
  • 陕西澄城樱桃在上海推介,向长三角消费者发出“甜蜜之邀”
  • “80后”计算机专家唐金辉已任南京林业大学副校长
  • 上任后首访,德国总理与法国总统举行会晤
  • 民生访谈|摆摊设点、公园搭帐篷、行道树飘絮,管理难题怎么解?
  • 马上评|演出服“穿过就退货”的闹剧不该一再重演