# 12.3. `shelve` - Python对象持久性

源代码： Lib / shelve.py

“shelf”是一个持久的，类似字典的对象。与“dbm”数据库的区别是，值（而不是键！）在一个架子上可以是基本上任意的Python对象 - `pickle`模块可以处理的任何东西。这包括大多数类实例，递归数据类型和包含大量共享子对象的对象。键是普通字符串。

`shelve.``open`(filename, flag='c', protocol=None, writeback=False)

  打开持久性字典。指定的文件名是基础数据库的基本文件名。作为副作用，可以向文件名添加扩展名，并且可以创建多个文件。默认情况下，打开底层数据库文件以进行读取和写入。可选的标志参数与`dbm.open()`的标志参数具有相同的解释。

  默认情况下，版本3 pickles 用于序列化值。可以使用协议参数指定pickle协议的版本。

  由于Python语义，shelf 不能知道何时修改了可变的持久化字典条目。默认情况下，修改的对象在分配到货架时写入（请参阅Example）。如果可选的回写参数设置为True，所有访问的条目也会缓存在内存中，并写回`sync()`和`close()`；这可以使得持久化字典中的可变条目变得更容易，但是，如果访问许多条目，则它可以消耗大量的高速缓存的存储器，并且它可以使得关闭操作非常缓慢，因为所有访问的条目被回写没有办法确定哪些被访问的条目是可变的，哪些被实际上被变异）。

  注意

  不要依靠架子自动关闭；当你不再需要它时，总是调用`close()`，或者使用`shelve.open()`作为上下文管理器：

      with shelve.open('spam') as db:
          db['eggs'] = 'eggs'

警告

由于`shelve`模块由`pickle`支持，因此从不受信任的来源加载搁架是不安全的。像pickle一样，加载货架可以执行任意代码。

Shelf对象支持字典支持的所有方法。这简化了从基于字典的脚本到需要持久存储的脚本的过渡。

支持两种其他方法：

`Shelf.``sync`()

  如果以writeback设置为`True`打开机架，则写回缓存中的所有条目。同时清空缓存并同步磁盘上的持久性字典（如果可行）。当搁架用`close()`关闭时会自动调用。

`Shelf.``close`()

  同步并关闭持久性dict对象。封闭货架上的操作将失败，并显示`ValueError`。

也可以看看

持久性字典配方具有广泛支持的存储格式并具有本地字典的速度。

## 12.3.1. Restrictions

- 将选择使用哪个数据库包（例如`dbm.ndbm`或`dbm.gnu`）取决于哪个接口可用。因此，使用`dbm`直接打开数据库是不安全的。数据库也是（不幸的是）受到`dbm`的限制，如果它被使用 - 这意味着存储在数据库中的对象（的腌制表示）应当相当小，在极少数情况下键冲突可能导致数据库拒绝更新。
- `shelve`模块不支持并发对搁置对象的读/写访问。（多个同时读取访问是安全的。）当一个程序有一个开放的书架，没有其他程序应该打开读或写。Unix文件锁定可以用来解决这个问题，但这在Unix版本中有所不同，需要了解所使用的数据库实现。

class `shelve.``Shelf`(dict, protocol=None, writeback=False, keyencoding='utf-8')

  `collections.abc.MutableMapping`的子类，用于在dict对象中存储pickled值。

  默认情况下，版本3pickles用于序列化值。可以使用协议参数指定pickle协议的版本。有关pickle协议的讨论，请参阅`pickle`文档。

  如果writeback参数为`True`，则对象将保存所有访问的缓存，并在同步和关闭时间将它们写回dict 。这允许对可变条目的自然操作，但可以消耗更多的内存，并使同步和关闭需要很长时间。

  keyencoding参数是用于在键与底层dict一起使用之前对其进行编码的编码。

  `Shelf`对象也可以用作上下文管理器，在`with`块结束时，它将自动关闭。

  在版本3.2中更改：添加了keyencoding参数；以前，密钥始终以UTF-8编码。

  在版本3.4中更改：添加了上下文管理器支持。

class `shelve.``BsdDbShelf`(dict, protocol=None, writeback=False, keyencoding='utf-8')

  A subclass of `Shelf` which exposes `first()`, `next()`, `previous()`, `last()` and `set_location()` which are available in the third-party `bsddb` module from pybsddb but not in other database modules. 传递给构造函数的dict对象必须支持这些方法。这通常通过调用`bsddb.hashopen()`，`bsddb.btopen()`或`bsddb.rnopen()`之一来完成。可选的协议，回写和keyencoding参数具有与`Shelf`类相同的解释。

class `shelve.``DbfilenameShelf`(filename, flag='c', protocol=None, writeback=False)

  `Shelf`的子类，它接受文件名而不是类似dict的对象。底层文件将使用`dbm.open()`打开。默认情况下，将创建并打开文件以进行读取和写入。可选的标志参数与`open()`函数具有相同的解释。可选的协议和回写参数具有与`Shelf`类相同的解释。

## 12.3.2. Example

要总结接口（`key`是一个字符串，`data`是一个任意对象）：

    import shelve

    d = shelve.open(filename)  # open -- file may get suffix added by low-level
                               # library

    d[key] = data              # store data at key (overwrites old data if
                               # using an existing key)
    data = d[key]              # retrieve a COPY of data at key (raise KeyError
                               # if no such key)
    del d[key]                 # delete data stored at key (raises KeyError
                               # if no such key)

    flag = key in d            # true if the key exists
    klist = list(d.keys())     # a list of all existing keys (slow!)

    # as d was opened WITHOUT writeback=True, beware:
    d['xx'] = [0, 1, 2]        # this works as expected, but...
    d['xx'].append(3)          # *this doesn't!* -- d['xx'] is STILL [0, 1, 2]!

    # having opened d without writeback=True, you need to code carefully:
    temp = d['xx']             # extracts the copy
    temp.append(5)             # mutates the copy
    d['xx'] = temp             # stores the copy right back, to persist it

    # or, d=shelve.open(filename,writeback=True) would let you just code
    # d['xx'].append(5) and have it work as expected, BUT it would also
    # consume more memory and make the d.close() operation slower.

    d.close()                  # close it

也可以看看

模块`dbm`
  `dbm`样式数据库的通用接口。
模块`pickle`
  `shelve`使用的对象序列化。