【问题标题】:Saving and loading objects and using pickle保存和加载对象以及使用pickle
【发布时间】:2011-05-30 15:48:44
【问题描述】:

我正在尝试使用 pickle 模块保存和加载对象。
首先我声明我的对象:

>>> class Fruits:pass
...
>>> banana = Fruits()

>>> banana.color = 'yellow'
>>> banana.value = 30

之后我打开一个名为“Fruits.obj”的文件(之前我创建了一个新的 .txt 文件并重命名了“Fruits.obj”):

>>> import pickle
>>> filehandler = open(b"Fruits.obj","wb")
>>> pickle.dump(banana,filehandler)

完成此操作后,我关闭了我的会话并开始了一个新会话,然后我放入了下一个会话(尝试访问它应该保存的对象):

file = open("Fruits.obj",'r')
object_file = pickle.load(file)

但我有这样的信息:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
ValueError: read() from the underlying stream did notreturn bytes

我不知道该怎么做,因为我不明白这条信息。 有谁知道我如何加载我的对象“香蕉”? 谢谢!

编辑: 正如你们中的一些人所建议的那样,我说:

>>> import pickle
>>> file = open("Fruits.obj",'rb')

没有问题,但是我放的下一个是:

>>> object_file = pickle.load(file)

我有错误:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python31\lib\pickle.py", line 1365, in load
encoding=encoding, errors=errors).load()
EOFError

【问题讨论】:

标签: python object pickle


【解决方案1】:

至于你的第二个问题:

 Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "C:\Python31\lib\pickle.py", line
 1365, in load encoding=encoding,
 errors=errors).load() EOFError

在您读取文件内容后,文件指针将位于文件末尾 - 将不再有数据可读取。您必须倒带文件,以便再次从头开始读取:

file.seek(0)

您通常想要做的是使用上下文管理器打开文件并从中读取数据。这样,文件将在块执行完成后自动关闭,这也将帮助您将文件操作组织成有意义的块。

最后,cPickle 是 C 中 pickle 模块的更快实现。所以:

In [1]: import _pickle as cPickle

In [2]: d = {"a": 1, "b": 2}

In [4]: with open(r"someobject.pickle", "wb") as output_file:
   ...:     cPickle.dump(d, output_file)
   ...:

# pickle_file will be closed at this point, preventing your from accessing it any further

In [5]: with open(r"someobject.pickle", "rb") as input_file:
   ...:     e = cPickle.load(input_file)
   ...:

In [7]: print e
------> print(e)
{'a': 1, 'b': 2}

【讨论】:

  • 这个 'd = {"a": 1, "b": 2}' 是什么样的数据结构?
  • @Peterstone: {"a": 1, "b": 2} 创建一个字典,其中包含 "a""b" 键。这在在线文档中称为dictionary display expression。这只是可以构造 dict 类型对象的几种不同方式之一,它是 Python 中可用的几种标准内置数据类型之一。
  • 为什么字母 'r' 继续文件名?我在文档中没有看到。此外,文件名很难使用变量。
  • 今天查看这个答案并注意到它仅适用于 Python 2.x。在 Python 3.x 中,应该直接使用pickle,如果可以的话,它会自动导入cpickledocs.python.org/3.1/whatsnew/3.0.html#library-changes
【解决方案2】:

您可以使用anycache 为您完成这项工作。假设您有一个创建实例的函数myfunc

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc()
    banana = Fruits()
    banana.color = 'yellow'
    banana.value = 30
return banana

Anycache 第一次调用myfunc 并将结果腌制到一个 cachedir 中的文件,使用唯一标识符(取决于函数名和参数)作为文件名。 在任何连续运行中,都会加载腌制对象。

如果 cachedir 在 python 运行之间保留,则腌制对象取自上一次 python 运行。

函数参数也被考虑在内。 重构的实现同样有效:

from anycache import anycache

class Fruits:pass

@anycache(cachedir='/path/to/your/cache')    
def myfunc(color, value)
    fruit = Fruits()
    fruit.color = color
    fruit.value = value
return fruit

【讨论】:

    【解决方案3】:

    您似乎想跨会话保存类实例,使用pickle 是一种不错的方法。但是,有一个名为klepto 的包将对象的保存抽象为字典接口,因此您可以选择腌制对象并将其保存到文件中(如下所示),或者腌制对象并将其保存到数据库中,或使用 json 或许多其他选项代替 pickle。 klepto 的好处在于,通过抽象为一个通用接口,它变得很容易,因此您不必记住如何通过酸洗到文件或其他方式保存的低级细节。

    请注意,它适用于动态添加的类属性,而 pickle 无法做到...

    dude@hilbert>$ python
    Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
    [GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from klepto.archives import file_archive 
    >>> db = file_archive('fruits.txt')
    >>> class Fruits: pass
    ... 
    >>> banana = Fruits()
    >>> banana.color = 'yellow'
    >>> banana.value = 30
    >>> 
    >>> db['banana'] = banana 
    >>> db.dump()
    >>> 
    

    然后我们重新开始……

    dude@hilbert>$ python
    Python 2.7.6 (default, Nov 12 2013, 13:26:39) 
    [GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from klepto.archives import file_archive
    >>> db = file_archive('fruits.txt')
    >>> db.load()
    >>> 
    >>> db['banana'].color
    'yellow'
    >>> 
    

    Klepto 适用于 python2 和 python3。

    在此处获取代码: https://github.com/uqfoundation

    【讨论】:

      【解决方案4】:

      以下对我有用:

      class Fruits: pass
      
      banana = Fruits()
      
      banana.color = 'yellow'
      banana.value = 30
      
      import pickle
      
      filehandler = open("Fruits.obj","wb")
      pickle.dump(banana,filehandler)
      filehandler.close()
      
      file = open("Fruits.obj",'rb')
      object_file = pickle.load(file)
      file.close()
      
      print(object_file.color, object_file.value, sep=', ')
      # yellow, 30
      

      【讨论】:

      • 这对我有用,但我追求的是关闭一个会话,打开一个新会话并加载我在过去会话中保存的内容。我在放置“filehandler.close()”行后关闭会话,然后打开一个新的,然后放置其余代码,然后在放置“object_file = pickle.load(file)”后出现此错误:Traceback (最近一次调用最后):文件“”,第 1 行,在 object_file = pickle.load(file) 文件“C:\Python31\lib\pickle.py”,第 1365 行,在加载编码中=encoding, errors=errors).load() AttributeError: 'module' object has no attribute 'Fruits'
      • @Peterstone:在第二个会话中,您需要定义class Fruits,以便pickle.load() 可以根据保存在二进制文件中的数据重构对象。此类事情的最佳实践是将class Fruits 定义放在单独的.py 文件中(使其成为自定义模块),然后在需要时将import 该模块或其中的项目(即两个会话)。例如,如果你把它放在一个名为MyDataDefs.py 的文件中,那么你可以写from MyDataDefs import Fruits。如果不清楚,请告诉我,我会相应地更新我的答案。
      • 实际上 PEP 8 建议使用 all lowercase characters 作为模块名称,所以我上一条评论末尾的示例应该在一个名为 my_data_defs.py 的文件中使用 from my_data_defs import Fruits
      【解决方案5】:

      您没有以二进制模式打开文件。

      open("Fruits.obj",'rb')
      

      应该可以。

      对于您的第二个错误,该文件很可能是空的,这意味着您无意中清空了它或使用了错误的文件名等。

      (这是假设您确实关闭了会话。如果没有,那是因为您没有在写入和读取之间关闭文件)。

      我测试了你的代码,它可以工作。

      【讨论】:

        【解决方案6】:

        您也忘记将其读取为二进制文件。

        在你的写作部分你有:

        open(b"Fruits.obj","wb") # Note the wb part (Write Binary)
        

        在阅读部分你有:

        file = open("Fruits.obj",'r') # Note the r part, there should be a b too
        

        所以替换为:

        file = open("Fruits.obj",'rb')
        

        它会起作用的:)


        至于您的第二个错误,很可能是由于未正确关闭/同步文件造成的。

        试试这段代码写:

        >>> import pickle
        >>> filehandler = open(b"Fruits.obj","wb")
        >>> pickle.dump(banana,filehandler)
        >>> filehandler.close()
        

        这个(不变的)阅读:

        >>> import pickle
        >>> file = open("Fruits.obj",'rb')
        >>> object_file = pickle.load(file)
        

        更简洁的版本是使用with 语句。

        写作:

        >>> import pickle
        >>> with open('Fruits.obj', 'wb') as fp:
        >>>     pickle.dump(banana, fp)
        

        阅读:

        >>> import pickle
        >>> with open('Fruits.obj', 'rb') as fp:
        >>>     banana = pickle.load(fp)
        

        【讨论】:

        • 我使用您的使用 with 语句的版本,我收到以下消息: Traceback(最近一次调用最后一次):文件“”,第 1 行,在 print(banana .color) AttributeError: 'Fruits' 对象没有属性 'color'
        【解决方案7】:

        在这种情况下总是以二进制模式打开

        file = open("Fruits.obj",'rb')
        

        【讨论】:

          猜你喜欢
          • 2011-06-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多