保存和加载对象以及使用pickle
Posted
技术标签:
【中文标题】保存和加载对象以及使用pickle【英文标题】:Saving and loading objects and using 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
【问题讨论】:
相关:Saving an Object (Data persistence in Python) 相关:How can I use pickle to save a dict? 【参考方案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
,如果可以的话,它会自动导入cpickle
。 docs.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 (最近一次调用最后):文件“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(最近一次调用最后一次):文件“在这种情况下总是以二进制模式打开
file = open("Fruits.obj",'rb')
【讨论】:
以上是关于保存和加载对象以及使用pickle的主要内容,如果未能解决你的问题,请参考以下文章