Python中使用Pickle和Shelve存储大型字典的比较

26 投票
2 回答
20868 浏览
提问于 2025-04-17 14:34

如果我把一个很大的目录存储为一个 pickle 文件,使用 cPickle 加载它时,是不是意味着所有的数据都会一次性加载到内存中?

如果是这样,有没有一种跨平台的方法,可以像 pickle 一样,但只按需访问每个条目(也就是说,避免一次性把整个字典加载到内存中,而是按名称逐个加载每个条目)?我知道 shelve 是为了这个目的而设计的:它的可移植性和 pickle 一样吗?

2 个回答

10

如果你想要一个比 shelve 更强大的模块,可以看看 kleptoklepto 是为了提供一个字典接口,方便在不同平台上存储数据到硬盘或数据库,并且它能处理大数据。

在这里,我们首先创建一些存储在硬盘上的“腌制”对象。它们使用 dir_archive,这个方法是把每个对象存储在一个单独的文件里。

>>> d = dict(zip('abcde',range(5)))
>>> d['f'] = max
>>> d['g'] = lambda x:x**2
>>> 
>>> import klepto
>>> help(klepto.archives.dir_archive)       

>>> print klepto.archives.dir_archive.__new__.__doc__
initialize a dictionary with a file-folder archive backend

    Inputs:
        name: name of the root archive directory [default: memo]
        dict: initial dictionary to seed the archive
        cached: if True, use an in-memory cache interface to the archive
        serialized: if True, pickle file contents; otherwise save python objects
        compression: compression level (0 to 9) [default: 0 (no compression)]
        memmode: access mode for files, one of {None, 'r+', 'r', 'w+', 'c'}
        memsize: approximate size (in MB) of cache for in-memory compression

>>> a = klepto.archives.dir_archive(dict=d)
>>> a
dir_archive('memo', {'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': <function <lambda> at 0x102f562a8>, 'f': <built-in function max>}, cached=True)
>>> a.dump()
>>> del a

现在,数据都在硬盘上了,我们可以选择想要加载到内存中的对象。b 是内存中的字典,而 b.archive 则把这些文件的集合映射成一个字典视图。

>>> b = klepto.archives.dir_archive('memo')
>>> b
dir_archive('memo', {}, cached=True)
>>> b.keys()   
[]
>>> b.archive.keys()
['a', 'c', 'b', 'e', 'd', 'g', 'f']
>>> b.load('a')
>>> b
dir_archive('memo', {'a': 0}, cached=True)
>>> b.load('b')
>>> b.load('f')
>>> b.load('g')
>>> b['g'](b['f'](b['a'],b['b']))
1

klepto 还提供了一个与 sql 存档相同的接口。

>>> print klepto.archives.sql_archive.__new__.__doc__
initialize a dictionary with a sql database archive backend

    Connect to an existing database, or initialize a new database, at the
    selected database url. For example, to use a sqlite database 'foo.db'
    in the current directory, database='sqlite:///foo.db'. To use a mysql
    database 'foo' on localhost, database='mysql://user:pass@localhost/foo'.
    For postgresql, use database='postgresql://user:pass@localhost/foo'. 
    When connecting to sqlite, the default database is ':memory:'; otherwise,
    the default database is 'defaultdb'. If sqlalchemy is not installed,
    storable values are limited to strings, integers, floats, and other
    basic objects. If sqlalchemy is installed, additional keyword options
    can provide database configuration, such as connection pooling.
    To use a mysql or postgresql database, sqlalchemy must be installed.

    Inputs:
        name: url for the sql database [default: (see note above)]
        dict: initial dictionary to seed the archive
        cached: if True, use an in-memory cache interface to the archive
        serialized: if True, pickle table contents; otherwise cast as strings

>>> c = klepto.archives.sql_archive('database')
>>> c.update(b)
>>> c
sql_archive('sqlite:///database', {'a': 0, 'b': 1, 'g': <function <lambda> at 0x10446b1b8>, 'f': <built-in function max>}, cached=True)
>>> c.dump()

现在,硬盘上的相同对象也在一个 sql 存档中。我们可以向任一存档中添加新的对象。

>>> b['x'] = 69
>>> c['y'] = 96
>>> b.dump('x')
>>> c.dump('y')

你可以在这里获取 kleptohttps://github.com/uqfoundation

26

我知道shelve是用来做这个的,但它的可移植性和pickle一样吗?

是的。shelvePython标准库的一部分,而且是用Python写的。

编辑

假设你有一个很大的字典:

bigd = {'a': 1, 'b':2, # . . .
}

如果你想保存它,而不想以后再把整个字典都读进来,那就不要用pickle来保存,保存成shelf会更好,shelf就像是一个存储在磁盘上的字典。

import shelve

myShelve = shelve.open('my.shelve')
myShelve.update(bigd)
myShelve.close()

然后你可以:

import shelve

myShelve = shelve.open('my.shelve')
value = myShelve['a']
value += 1
myShelve['a'] = value

你基本上可以把shelve对象当作字典来使用,但里面的内容是存储在磁盘上的(作为单独的pickle文件),需要的时候再读取。

如果你的对象可以作为属性列表来存储,那么sqlite可能是一个不错的选择。shelve和pickle很方便,但只能通过Python访问,而sqlite数据库可以被大多数编程语言读取。

撰写回答