本文对shelve模块进行说明。

shelve模块实现了对任意可被pickle的python对象进行持久存储,并提供了类字典的API使用。

简介

当使用关系数据库太重时,shelve模块可以为python对象提供了一个简单的持久的存储选择。它就像字典一样,通过关键字访问shelf对象,其值经过pickle,写入anydbm创建和管理的数据库。在使用时,可以自己定义一些存储结构使其存储较复杂的数据。

创建shelf对象

最简单的使用shelve模块的方式是通过DbfilenameShelf类。使用函数shelve.open()(使用的是anydbm)来存储数据。可以使用类或简单的调用:

import shelve

s = shelve.open('test_shelf.db')  # 创建test_shelf.db文件进行存储
try:
    s['key1'] = {'int':10, 'float':9.5, 'string': 'Sample Data'} # 写入key1,值可以是python支持的对象,看起来和字典的用法一样
finally:
    s.close()

访问已存储的数据,打开shelf,可以像字典一样进行使用。

s = shelve.open('test_shelf.db')  # 打开已存在的数据文件
try:
    existing = s['key1'] # 获取之前存储的key1
finally:
    s.close()

print(existing)  # 打印s['key1'],会得到刚才存储的数据

执行结果:

$ python shelve_create.py
$ python shelve_existing.py
{'int': 10, 'float': 9.5, 'string': 'Sample data'}

dbm模块不支持多个应用同时写入同一数据库,如果客户端或程序不会修改shelf,请指定shelve以只读方式打开数据库。

s = shelve.open('test_shelf.db', flag='r')   # 打开时指定flag=‘r',只读模式打开
try:
    existing =  s['key1']
finally:
    s.close()

print existing

当数据库以只读模式打开,但又试图更改数据库时,会引起一个访问出错异常。这一异常类型依赖于在创建数据库时被anydbm选择的数据库模块。

写回

默认情况下,Shelves不去追踪可变对象的修改。意思就是,如果你改变了已存储在shelf中的一个项目的内容,就必须重新存储该项目来更新shelf.

s = shelve.open('test_shelf.db')
try:
    print s['key1']
    s['key']['new_value'] = 'this is not here before'
finally:
    s.close()

s = shelve.open('test_shelf.db', writeback = True) # 设置写回或同步
try:
   print s['key1']
finally:
    s.close()

在这个例子中,没有对字典的关键字key1的内容进行存储,虽然打开时设置了writeback,因此重新打开shelf时,key1内容没有改变。

$ python shelve_create.py
$ python shelve_withoutwriteback.py
{'int': 10, 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'float': 9.5, 'string': 'Sample data'}

为了自动捕捉存储在shelf中的可变对象所发生的改变,需改变前设置writeback可用.writeback标志导致shelf使用一缓存来记住从数据库中调出的所有对象。当shelf关闭的时候,每一个缓存中的对象也重新写回数据库。

s = shelve.open('test_shelf.db', writeback=True)
try:
    print(s['key1'])
    s['key1']['new_value'] = 'this was not here before'
    print(s['key1'])
finally;
    s.close()

s = shelve.open('test_shelf.db', writeback=True)
try:
    print(s['key1'])
finally:
    s.close()

执行结果:

$ python shelve_create.py
$ python shelve_writeback.py
{'int': 10, 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'new_value': 'this was not here before', 'float': 9.5, 'string': 'Sample data'}
{'int': 10, 'new_value': 'this was not here before', 'float': 9.5, 'string': 'Sample data'}

虽然使用writeback模式可用减少出错几率,也能更加透明化对象的持久性。但是,不是每种情况都要使用writeback模式。原因看到前面的示例,应该能猜到。当shelf打开时,缓存就要占用额外的内存。 并且,当shelf关闭时,同样会对缓存中的对象再次写回数据库,增加了开销。即使没有进行改变,仍然会进行数据写回。比较好的方式是,确认需要写入数据时,再设置writeback,读数据时设置只读或默认即可。

指定shelf类型

上面的例子全部使用默认的shelf实现。使用shelve.open()直接代替一种shelf实现,是常见用法, 尤其是在不关心用哪种数据库存储数据的时候. 然而, 有时常会关心它. 如果是在这种情况下, 通常就会直接使用DbfilenameShelf或者BsdDbShelf, 更或者是通过Shelf的子类来解决问题.

by 李鹏