Сохранение и загрузка нескольких объектов в файле pickle?

У меня есть класс, который обслуживает игроков в игре, создает их и другие вещи.

Мне нужно сохранить эти объекты проигрывателя в файл, чтобы использовать его позже. Я пробовал модуль pickle, но я не знаю, как сохранить несколько объектов и снова загрузить их? Есть ли способ сделать это, или я должен использовать другие классы, такие как списки и сохранять и загружать мои объекты в списке?

Есть ли способ лучше?

Ответ 1

Использование списка, кортежа или 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)

Что печатает:

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

Однако файл 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

Если вы это сделаете, вы несете ответственность за знание того, сколько соленья находится в файле, который вы выписываете. Вышеприведенный код делает это, сначала пробивая количество объектов списка.

Ответ 2

Два дополнения к принятому ответу Тима Питерса.

Во-первых, вам не нужно хранить количество выбранных вами предметов отдельно, если вы остановите загрузку, когда дойдете до конца файла:

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

items = loadall(myfilename)

Это предполагает, что файл содержит только соленые огурцы; если там есть что-то еще, генератор попытается обработать все остальное как огурцы, что может быть опасно.

Во-вторых, таким образом, вы получаете не список, а генератор. Это позволит загружать в память только один элемент за раз, что полезно, если выгружаемые данные очень велики - одна из возможных причин, по которым вы, возможно, захотели сначала выбрать несколько элементов по отдельности. Вы все еще можете перебирать items с циклом for как если бы это был список.

Ответ 3

Попробуй это:

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()

Ответ 4

Я дам объектно-ориентированную демонстрацию с помощью 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]

Ответ 5

Это легко, если вы используете klepto, что дает вам возможность прозрачно хранить объекты в файлах или базах данных. Он использует API-интерфейс dict и позволяет вам делать 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
>>>

Ответ 6

Если вы дампируете его итеративно, вам придется читать его итеративно.

Вы можете запустить цикл (как показано в принятом ответе), чтобы не допускать сортировку строк до тех пор, пока не достигнете конца файла (после чего возникает EOFError).

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}]