在pickle文件中保存和加载多个对象?


68

我有一堂课,为游戏中的玩家提供服务,创建他们以及其他东西。

我需要将这些播放器对象保存在文件中,以便以后使用。我已经尝试过pickle模块,但是我不知道如何保存多个对象并再次加载它们?有没有办法做到这一点,还是应该使用其他类(例如列表)并将对象保存并加载到列表中?

有没有更好的办法?


2
使用list集装箱似乎是合理的。
falsetru

一年后问。我们不能将python的货架库用于同一任务。如果不是这样的话,那将是一个缺点
阿曼·拉帕里亚

您可以用来检查泡菜文件的方法:pickleviewer.com
Christo S.

Answers:


78

到目前为止,使用列表,元组或字典是最常见的方法:

import pickle
PIK = "pickle.dat"

data = ["A", "b", "C", "d"]
with open(PIK, "wb") as f:
    pickle.dump(data, f)
with open(PIK, "rb") as f:
    print pickle.load(f)

打印:

['A', 'b', 'C', 'd']

但是,泡菜文件可以包含任意数量的泡菜。这是产生相同输出的代码。但是请注意,它很难编写和理解:

with open(PIK, "wb") as f:
    pickle.dump(len(data), f)
    for value in data:
        pickle.dump(value, f)
data2 = []
with open(PIK, "rb") as f:
    for _ in range(pickle.load(f)):
        data2.append(pickle.load(f))
print data2

如果这样做,您有责任知道所写出的文件中有多少个泡菜。上面的代码通过首先腌制列表对象的数量来做到这一点。


谢谢,我有你的主意,但我认为对于多个列表对象可能会导致内存问题,并且我决定将每个播放器保存在单独的文件中,但是您认为列出泡菜对象会导致内存问题吗?
hamidfzm 2013年

1
没有足够的信息。有几名球员?每个球员的泡菜有多大?有多少可用RAM?如果您有很多玩家,那么最好合并一个数据库并在其中存储泡菜(而不是发明自己的数据库,一次又一个痛苦的步骤)。
蒂姆·彼得斯

1
为什么所有泡菜示例始终使用二进制模式?二进制文件写入是我的工作尚未突破的一个领域……我对此一无所知,也不知道为什么有人在任何地方使用它。
临时用户

3
使用@Aerovistae二进制模式是因为Windows在文本模式下会导致行尾字符混乱。
Compie

118

蒂姆·彼得斯接受的答案有两个补充。

首先,如果在到达文件末尾时停止加载,则无需存储单独腌制的项目数:

def loadall(filename):
    with open(filename, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

items = loadall(myfilename)

假设文件仅包含泡菜;如果里面还有其他东西,发生器将尝试把里面的其他东西都当作泡菜,这很危险。

其次,通过这种方式,您不会得到列表,而是生成器。一次只能将一个项目加载到内存中,这在转储的数据非常大时很有用-这可能是您可能想首先腌制多个项目的可能原因。您还可以遍历items一个for循环就好像它是一个列表。


7
这应该是最佳答案
Kristopher Wagner

3
请注意,load(myfilename)在迭代结果之前,调用实际上不会加载数据或从文件中读取数据。如果要立即加载它们,请使用list(load(myfilename))for循环之类的方法。
drevicko

1
在生成器刚好被垃圾回收之前,这种方法会不会使文件句柄保持打开状态,从而导致潜在的锁定问题?为了解决这个问题,我们应该把yield 外面with open()块?允许这样做会导致不必要的读取来遍历pickle文件,但我认为我宁愿使用它来悬挂文件句柄。除非我们确定此方法始终会迅速被EOF调用,否则我们将在到达文件末尾时关闭文件。(但是,如果我们不愿意产生单个元素,那可能是因为我们不需要解开文件中的所有对象。)
克里斯(Chris

2
@Chris:如果迭代器用完了,with open它将终止并正确关闭文件。如果它可能用不完,我们通常不会在意打开的文件。如果它可能没有被使用到最后并且我们不喜欢打开的文件,那么是的,上面的构造并不是最好的方法。
Lutz Prechelt '18

18

尝试这个:

import pickle

file = open('test.pkl','wb')
obj_1 = ['test_1', {'ability', 'mobility'}]
obj_2 = ['test_2', {'ability', 'mobility'}]
obj_3 = ['test_3', {'ability', 'mobility'}]

pickle.dump(obj_1, file)
pickle.dump(obj_2, file)
pickle.dump(obj_3, file)

file.close()

file = open('test.pkl', 'rb')
obj_1 = pickle.load(file)
obj_2 = pickle.load(file)
obj_3 = pickle.load(file)
print(obj_1)
print(obj_2)
print(obj_3)
file.close()

6

我将提供一个pickle用于存储和还原一个或多个对象的面向对象的演示object

class Worker(object):

    def __init__(self, name, addr):
        self.name = name
        self.addr = addr

    def __str__(self):
        string = u'[<Worker> name:%s addr:%s]' %(self.name, self.addr)
        return string

# output one item
with open('testfile.bin', 'wb') as f:
    w1 = Worker('tom1', 'China')
    pickle.dump(w1, f)

# input one item
with open('testfile.bin', 'rb') as f:
    w1_restore = pickle.load(f)
print 'item: %s' %w1_restore

# output multi items
with open('testfile.bin', 'wb') as f:
    w1 = Worker('tom2', 'China')
    w2 = Worker('tom3', 'China')
    pickle.dump([w1, w2], f)

# input multi items
with open('testfile.bin', 'rb') as f:
    w_list = pickle.load(f)

for w in w_list:
    print 'item-list: %s' %w

输出:

item: [<Worker> name:tom1 addr:China]
item-list: [<Worker> name:tom2 addr:China]
item-list: [<Worker> name:tom3 addr:China]

3

如果要迭代转储,则还必须迭代读取。

您可以运行一个循环(如已接受的答案所示),以保持不分批处理行,直到到达文件末尾(此时EOFError引发an )。

data = []
with open("data.pickle", "rb") as f:
    while True:
        try:
            data.append(pickle.load(f))
        except EOFError:
            break

最小的可验证示例

import pickle

# Dumping step
data = [{'a': 1}, {'b': 2}]
with open('test.pkl', 'wb') as f:
    for d in data:
        pickle.dump(d, f)

# Loading step
data2 = []
with open('test.pkl', 'rb') as f:
    while True:
        try:
            data2.append(pickle.load(f))
        except EOFError:
            break

data2
# [{'a': 1}, {'b': 2}]

data == data2
# True

当然,这是基于您的对象必须单独腌制的假设。 您还可以将数据存储为单个对象列表,然后使用单个pickle / unpickle调用(无需循环)。

data = [{'a':1}, {'b':2}]  # list of dicts as an example
with open('test.pkl', 'wb') as f:
    pickle.dump(data, f)

with open('test.pkl', 'rb') as f:
    data2 = pickle.load(f)

data2
# [{'a': 1}, {'b': 2}]

0

如果使用klepto,这很容易,它使您能够透明地将对象存储在文件或数据库中。它使用dict API,并允许您dump和/或load存档中的特定条目(在以下情况下,序列化的对象在名为的目录中每个文件存储一个条目scores)。

>>> import klepto
>>> scores = klepto.archives.dir_archive('scores', serialized=True)
>>> scores['Guido'] = 69 
>>> scores['Fernando'] = 42
>>> scores['Polly'] = 101
>>> scores.dump()
>>> # access the archive, and load only one 
>>> results = klepto.archives.dir_archive('scores', serialized=True)
>>> results.load('Polly')
>>> results
dir_archive('scores', {'Polly': 101}, cached=True)
>>> results['Polly']
101
>>> # load all the scores
>>> results.load()
>>> results['Guido']
69
>>>

0

以下是使用泡菜转储两个(或更多字典)并将其提取回去的方法:

import pickle

dict_1 = {1: 'one', 2: 'two'}
dict_2 = {1: {1: 'one'}, 2: {2: 'two'}}

F = open('data_file1.pkl', 'wb')
pickle.dump(dict_1, F)
pickle.dump(dict_2, F)
F.close()

=========================================

import pickle

dict_1 = {1: 'one', 2: 'two'}
dict_2 = {1: {1: 'one'}, 2: {2: 'two'}}

F = open('data_file1.pkl', 'rb')
G = pickle.load(F)
print(G)
H = pickle.load(F)
print(H)
F.close()

0

假设我们已将对象保存在Employee类的文件中。这是读取文件中所有对象的代码:

 e = Employee()    

with open(filename, 'rb') as a:
    while True:
        try:
            e = pickle.load(a)
            e.ShowRecord()
        except EOFError:
            break    
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.