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

Ham*_*FzM 55 python class object pickle python-3.x

我有一个课程,为游戏中的玩家提供服务,创建他们和其他东西.

我需要将这些播放器对象保存在文件中以便以后使用.我尝试了pickle模块,但我不知道如何保存多个对象并再次加载它们?有没有办法做到这一点,或者我应该使用其他类,如列表,并保存和加载我的对象列表?

有没有更好的办法?

Lut*_*elt 96

蒂姆·彼得斯接受了两个补充.

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

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

items = loadall(myfilename)
Run Code Online (Sandbox Code Playgroud)

这假设文件只包含泡菜; 如果那里还有其他任何东西,那么发电机也会试着把那里的其他任何东西当作泡菜来处理,这可能很危险.

其次,这样,你不会得到一个列表,而是一个生成器.这样一次只能将一个项目加载到内存中,这在转储数据非常大时非常有用 - 这可能是您可能想要首先单独挑选多个项目的一个原因.您还可以遍历items一个for循环就好像它是一个列表.

  • 这应该是最好的答案 (5认同)
  • 请注意,调用`load(myfilename)`实际上不会加载数据或从文件中读取数据,直到您遍历结果为止。如果要立即加载它们,请使用“ list(load(myfilename))”或“ for”循环之类的东西。 (2认同)
  • 在生成器恰好被垃圾收集之前,这种方法不会使文件句柄保持打开状态,从而导致潜在的锁定问题吗?为了解决这个问题,我们应该把 `yield` 放在 `with open()` 块的*外面吗?当然,这会导致不必要的读取来迭代 pickle 文件,但我认为我更喜欢这样做而不是悬挂文件句柄。除非我们确定这个方法总是会被快速调用到 EOF,并且当到达文件末尾时我们关闭文件。(但是,如果我们费心生成单个元素,可能是因为我们不需要取消文件中的所有对象。) (2认同)
  • @Chris:如果迭代器用完了,“ with open”将终止并正确关闭文件。如果它可能用不完,我们通常不会在意打开的文件。如果它可能没有被使用到最后,并且我们不喜欢打开的文件,那么,是的,上述构造不是最好的方法。 (2认同)

Tim*_*ers 63

使用list,tuple或dict是目前最常用的方法:

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)
Run Code Online (Sandbox Code Playgroud)

打印:

['A', 'b', 'C', 'd']
Run Code Online (Sandbox Code Playgroud)

但是,pickle文件可以包含任意数量的pickle.这是产生相同输出的代码.但请注意,写作和理解起来比较困难:

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
Run Code Online (Sandbox Code Playgroud)

如果你这样做,你有责任知道你写的文件中有多少泡菜.上面的代码通过首先挑选列表对象的数量来做到这一点.

  • 没有足够的信息。有多少玩家?每个玩家的泡菜有多大?有多少内存可用?如果你有很多玩家,最好合并一个数据库并在其中存储泡菜(而不是发明自己的数据库,一次一个痛苦的步骤)。 (2认同)
  • 使用@Aerovistae二进制模式是因为Windows会在文本模式下弄乱行尾字符. (2认同)

小智 10

试试这个:

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()
Run Code Online (Sandbox Code Playgroud)


cs9*_*s95 6

如果你迭代地转储它,你也必须迭代地阅读它。

您可以运行一个循环(如已接受的答案所示)以保持解压行,直到到达文件末尾(此时 anEOFError被引发)。

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

最小可验证示例

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
Run Code Online (Sandbox Code Playgroud)

当然,这是在您的对象必须单独腌制的假设下。 您还可以将数据存储为单个对象列表,然后使用单个 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}]
Run Code Online (Sandbox Code Playgroud)


lyf*_*ing 5

我将给出一个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
Run Code Online (Sandbox Code Playgroud)

输出:

item: [<Worker> name:tom1 addr:China]
item-list: [<Worker> name:tom2 addr:China]
item-list: [<Worker> name:tom3 addr:China]
Run Code Online (Sandbox Code Playgroud)