将文件映射到内存

时间:2017-01-04 17:15:26

标签: python pickle

考虑一个文件,其内容是一个pickle python对象。为了使事情具体化,假设这个对象只是一个字符串列表,但实际上它更复杂。

我的python脚本在启动时读取该文件。在此之后,某些事件会触发读取对象的次要更新(在示例中,在列表中的任何位置添加或删除字符串)。为了使文件保持最新,脚本将对象进行pickle并将其写入文件(即整个文件是从头开始编写的)。

有没有办法避免每次都重写整个对象?我对其他存储数据的方式持开放态度,即不依赖于酸洗。我想我正在寻找将文件映射到内存的一些方法,以便对内存中对象的任何更新都会导致文件的即时更新。

1 个答案:

答案 0 :(得分:1)

update tl; dr

  • 是的,只有字符串变量适用于索引
  • 对于每个键值对,对值的任何更改都需要将整个值重新写入磁盘
    • 您使用的密钥越多,值越小,磁盘写入越小

如果您已经使用泡菜,我会推荐pythons的标准shelve库。它允许您设置dbm样式数据库并读/写任何pickleable对象

来自文档:

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 = d.has_key(key)   # true if the key exists
klist = d.keys() # a list of all existing keys (slow!)

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

# 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

根据评论,让我解释一下......

字符串为键:

就像在锡上说的那样,你只能用字符串索引键 - 值对。这与普通词典不同,它可以将任何可清除类型作为键。例如:

import shelve
database = shelve.open(filename) #open our database
dictionary = {} #create an empty dict

ingredients = ['flour', 'sugar', 'butter', 'water'] #recipe for something tasty

database['pie dough'] = ingredients #works because 'pie dough' is a string
dictionary['pie dough'] = ingredients #works because strings are hashable

recipe = 'pie dough'
database[recipe] = ingredients #you can use a variable as long as it is of <type 'str'>

#say we want to just number our entries..
database[5] = ingredients #won't work because 5 is an integer not a string
dictionary[5] = ingredients #works because integers are hashable

database.close()

可变对象

有关可变对象的文档中的声明使用了一些技术措辞,但通常是指您的数据驻留在ram vs与硬盘驱动器上的结果。基本上,如果你的一个对象是一个列表(一个可变对象),你可以像这样检索它:

mylist = database['pie dough']
#or
mylist = dictionary['pie dough']

我们已经意识到我们需要在这个配方中添加盐,而我们的对象是一个列表,所以我们可以相对轻松地调用:

mylist.append('salt')

然后我们可以使用简单的赋值将这个新的更新的配方写回数据库或字典

database['pie dough'] = mylist
#or
dictionary['pie dough'] = mylist

此方法将始终适用于数据库和字典。当您想直接引用对象时,会出现差异。当我们可以引用字典中包含的列表本身时,将mylist变量简单地创建为临时变量是多余的:

dictionary['pie dough'].append('salt')

这是直接导致字典中的值是对ram中原始对象的引用。修改dictionary['pie dough']时,您还要修改mylistingredients,因为它们实际上是同一个对象。加载对象时使用数据库:mylist = database['pie dough']您正在复制硬盘驱动器上的内容。您可以随意修改该副本,但它不会改变硬盘驱动器上的内容,因为它不是同一个对象(它是副本)。

解决方法是使用关键字writeback=True打开数据库。这提供了一些类似的功能,但有一些注意事项......虽然现在可以直接在字典对象上调用append(...)而无需复制,但在您调用dictionary.sync()或{{dictionary.close()之前,更改不会显示1}}。为此,我将从一个新的例子开始:

import shelve

d = shelve.open(filename)
d['dough'] = ['flour', 'sugar', 'butter', 'water']
d.close() #write initial recipe to our database

#some time later we want to update the recipe
d = shelve.open(filename, writeback=True)
d['dough'].append('salt')
d.close()

现在,每次访问部分数据库时,该部分都被加载到ram中,并且可以直接对该对象进行更新。对此的一个重要警告是,在调用d.close()之前,更新不会写入硬盘驱动器。数据库通常比系统中所有可用的ram都大,并且尝试更新太多对象而不调用d.sync()来写入挂起的更改到硬盘驱动器或定期关闭并重新打开数据库可能导致你内存不足的问题。