【问题标题】:Saving and loading multiple objects in pickle file?在泡菜文件中保存和加载多个对象?
【发布时间】:2014-01-10 02:15:49
【问题描述】:

我有一个类为游戏中的玩家服务,创建他们和其他东西。

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

有没有更好的办法?

【问题讨论】:

  • 使用list作为容器似乎是合理的。
  • 一年后询问。我们不能将 python 的搁置库用于相同的任务。如果不是,那将是什么缺点
  • 可以用来检查泡菜文件的东西:pickleviewer.com

标签: python class object python-3.x pickle


【解决方案1】:

使用列表、元组或字典是迄今为止最常用的方法:

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

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

【讨论】:

  • 谢谢你的想法,但我认为多个列表对象可能会导致内存问题,我决定将每个播放器保存在单独的文件中,但你认为列出泡菜对象会导致内存问题吗?跨度>
  • 没有足够的信息。有多少玩家?每个玩家的泡菜有多大?有多少 RAM 可用?如果您有很多玩家,最好合并一个数据库并在其中存储泡菜(而不是发明自己的数据库,一次是痛苦的一步)。
  • 为什么所有的pickle示例总是使用二进制模式?二进制文件写入是我的工作尚未触及的一个前沿领域……我对此一无所知,也不知道为什么有人在任何地方使用它。
  • 使用 @Aerovistae 二进制模式是因为 Windows 会在文本模式下处理行尾字符。
【解决方案2】:

对Tim Peters' accepted answer 的两个补充。

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

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

items = loadall(myfilename)

这假设文件只包含泡菜;如果里面还有其他东西,生成器会尝试将里面的其他东西也视为泡菜,这可能很危险。

第二,这样,你得到的不是列表,而是generator。 这一次只会将一项加载到内存中,这很有用 如果转储的数据非常大 - 您可能的一个可能原因 一开始就想分别腌制多个项目。 您仍然可以使用 for 循环遍历 items,就好像它是 一个列表。

【讨论】:

  • 这应该是最佳答案
  • 请注意,调用load(myfilename) 实际上并不会加载数据或从文件中读取,直到您遍历结果。如果您想立即加载它们,请使用list(load(myfilename)) 或for 循环。
  • 这种方法会不会让文件句柄保持打开状态,直到生成器恰好被垃圾回收,从而导致潜在的锁定问题?为了解决这个问题,我们应该把yield 放在 with open() 块之外吗?当然,这会导致不必要的读取来遍历泡菜文件,但我认为我更喜欢这个而不是悬空文件句柄。除非我们确定这个方法总是会被快速调用到 EOF,并且我们在到达文件末尾时关闭文件。 (但如果我们费心生成单个元素,那可能是因为我们不需要解开文件中的所有对象。)
  • @Chris:如果迭代器用完了,with open 将终止并正确关闭文件。如果它可能没有用完,我们通常不会关心打开的文件。如果它可能没有用完并且我们不喜欢打开的文件,那么,是的,上面的构造不是最好的方法。
  • @starriet:因为它为调用者省去了自己打开和关闭文件的麻烦,因此允许在使用泡菜的地方编写简单而惯用的代码。
【解决方案3】:

我将给出一个面向对象的演示,使用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]

【讨论】:

    【解决方案4】:

    如果您使用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
    >>>
    

    【讨论】:

      【解决方案5】:

      试试这个:

      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】:

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

        您可以运行一个循环(如 accepted answer 所示)保持对行进行取消处理,直到到达文件末尾(此时会引发 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}]
        

        【讨论】:

          【解决方案7】:

          这里是如何使用 pickle 转储两个(或多个字典),然后将其提取回来:

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

          【讨论】:

            【解决方案8】:

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

             e = Employee()    
            
            with open(filename, 'rb') as a:
                while True:
                    try:
                        e = pickle.load(a)
                        e.ShowRecord()
                    except EOFError:
                        break    
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-01-29
              • 2014-08-10
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-01-06
              • 1970-01-01
              相关资源
              最近更新 更多