我创建了一个这样的对象:
company1.name = 'banana'
company1.value = 40
我想保存这个对象。我怎么能这样做?
答案 0 :(得分:375)
您可以使用标准库中的pickle
模块。
以下是它的示例的基本应用:
import pickle
class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value
with open('company_data.pkl', 'wb') as output:
company1 = Company('banana', 40)
pickle.dump(company1, output, pickle.HIGHEST_PROTOCOL)
company2 = Company('spam', 42)
pickle.dump(company2, output, pickle.HIGHEST_PROTOCOL)
del company1
del company2
with open('company_data.pkl', 'rb') as input:
company1 = pickle.load(input)
print(company1.name) # -> banana
print(company1.value) # -> 40
company2 = pickle.load(input)
print(company2.name) # -> spam
print(company2.value) # -> 42
您还可以定义自己的简单实用程序,如下所示,它打开一个文件并将单个对象写入其中:
def save_object(obj, filename):
with open(filename, 'wb') as output: # Overwrites any existing file.
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
# sample usage
save_object(company1, 'company1.pkl')
由于这是一个非常受欢迎的答案,我想谈谈一些稍微高级的使用主题。
cPickle
(或_pickle
)vs pickle
实际使用cPickle
模块而不是pickle
几乎总是更可取,因为前者是用C语言编写的,速度要快得多。它们之间存在一些细微差别,但在大多数情况下它们是等效的,C版本将提供极大的优越性能。切换到它可能会更容易,只需将import
语句更改为:
import cPickle as pickle
在Python 3中,cPickle
已重命名为_pickle
,但由于pickle
模块现在自动执行此操作,因此不再需要执行此操作 - 请参阅What difference between pickle and _pickle in python 3?。
您可以使用以下内容来确保您的代码始终使用C版本,当它在Python 2和3中都可用时:
try:
import cPickle as pickle
except ModuleNotFoundError:
import pickle
pickle
可以读取和写入几种不同的,特定于Python的格式的文件,称为协议。 “协议版本0”是ASCII,因此是“人类可读的”。版本> 1是二进制的,可用的最高版本取决于正在使用的Python版本。默认值还取决于Python版本。在Python 2中,默认值是协议版本0
,但在Python 3.6中,它是协议版本3
。在Python 3.x中,模块添加了pickle.DEFAULT_PROTOCOL
,但在Python 2中不存在。
幸运的是,在每个调用中都有写pickle.HIGHEST_PROTOCOL
的简写(假设这是你想要的,而且你通常这样做),只需使用文字数-1
- 类似于引用序列的最后一个元素负指数。
所以,而不是写:
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
你可以写:
pickle.dump(obj, output, -1)
无论哪种方式,如果你创建了一个Pickler
对象用于多个pickle操作,你只需要指定一次协议:
pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
etc...
虽然一个pickle文件可以包含任意数量的pickle对象,如上面的示例所示,当它们的数目未知时,通常更容易将它们存储在某种变化中 - 大小的容器,如list
,tuple
或dict
,并在一次调用中将它们全部写入文件:
tech_companies = [
Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')
然后使用以下命令恢复列表及其中的所有内容:
with open('tech_companies.pkl', 'rb') as input:
tech_companies = pickle.load(input)
主要的优点是您不需要知道保存了多少对象实例以便以后加载它们(尽管这样做 可能,它需要一些稍微专业的代码)。有关不同方法的详细信息,请参阅相关问题Saving and loading multiple objects in pickle file?的答案。个人我喜欢@Lutz Prechelt answer最好的。这是适应这里的例子:
class Company:
def __init__(self, name, value):
self.name = name
self.value = value
def pickled_items(filename):
""" Unpickle a file of pickled data. """
with open(filename, "rb") as f:
while True:
try:
yield pickle.load(f)
except EOFError:
break
print('Companies in pickle file:')
for company in pickled_items('company_data.pkl'):
print(' name: {}, value: {}'.format(company.name, company.value))
答案 1 :(得分:42)
我认为假设对象是class
是一个非常强大的假设。如果它不是class
怎么办?还假设对象未在解释器中定义。如果在解释器中定义了怎么办?另外,如果动态添加属性怎么办?当一些python对象在创建后将__dict__
添加到pickle
时,pickle
不尊重这些属性的添加(即'忘记'它们被添加 - 因为pickle
序列化了引用对象定义)。
在所有这些情况下,cPickle
和object
可能会让您失望。
如果您要保存dill
(任意创建),您拥有属性(在对象定义中添加,或之后添加)...您最好的选择是使用Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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.
>>> import pickle
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>> with open('company.pkl', 'wb') as f:
... pickle.dump(company1, f, pickle.HIGHEST_PROTOCOL)
...
>>>
,这可以在python中序列化几乎任何东西。
我们从课程开始......
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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.
>>> import pickle
>>> with open('company.pkl', 'rb') as f:
... company1 = pickle.load(f)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1090, in load_global
klass = self.find_class(module, name)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/pickle.py", line 1126, in find_class
klass = getattr(mod, name)
AttributeError: 'module' object has no attribute 'Company'
>>>
现在关闭,然后重启......
pickle
哎呀... dill
无法处理它。我们试试lambda
。我们将引入另一个对象类型(Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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.
>>> import dill
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>>
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>>
>>> with open('company_dill.pkl', 'wb') as f:
... dill.dump(company1, f)
... dill.dump(company2, f)
...
>>>
)以获得良好的衡量标准。
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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.
>>> import dill
>>> with open('company_dill.pkl', 'rb') as f:
... company1 = dill.load(f)
... company2 = dill.load(f)
...
>>> company1
<__main__.Company instance at 0x107909128>
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>>
现在阅读文件。
pickle
有效。 dill
失败,而dill
没有失败的原因是__main__
将pickle
视为模块(大多数情况下),也可以选择类定义而不是通过引用进行酸洗(如dill
那样)。 lambda
可以腌制Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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.
>>> import dill
>>> class Company:
... pass
...
>>> company1 = Company()
>>> company1.name = 'banana'
>>> company1.value = 40
>>>
>>> company2 = lambda x:x
>>> company2.name = 'rhubarb'
>>> company2.value = 42
>>>
>>> dill.dump_session('dill.pkl')
>>>
的原因是它给它一个名字......然后就会发生酸洗魔法。
实际上,有一种更简单的方法来保存所有这些对象,特别是如果你创建了很多对象。只需转储整个python会话,稍后再回来。
Python 2.7.8 (default, Jul 13 2014, 02:29:54)
[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.
>>> import dill
>>> dill.load_session('dill.pkl')
>>> company1.name
'banana'
>>> company1.value
40
>>> company2.name
'rhubarb'
>>> company2.value
42
>>> company2
<function <lambda> at 0x1065f2938>
现在关闭你的电脑,享用浓咖啡或其他什么,然后再回来......
dill
唯一的主要缺点是dill
不是python标准库的一部分。因此,如果您无法在服务器上安装python包,则无法使用它。
但是,如果您能够在系统上安装python软件包,则可以使用git+https://github.com/uqfoundation/dill.git@master#egg=dill
获取最新的pip install dill
。您可以使用{{1}}获取最新发布的版本。
答案 2 :(得分:3)
您可以使用anycache为您完成这项工作。它考虑了所有细节:
pickle
模块来处理lambda
和所有的好处
python功能。假设您有一个创建实例的函数myfunc
:
from anycache import anycache
class Company(object):
def __init__(self, name, value):
self.name = name
self.value = value
@anycache(cachedir='/path/to/your/cache')
def myfunc(name, value)
return Company(name, value)
Anycache第一次调用myfunc
并将结果调到a
cachedir
中的文件使用唯一标识符(取决于函数名称及其参数)作为文件名。
在任何连续运行中,加载pickle对象。
如果在python运行之间保留cachedir
,那么pickle对象将从之前的python运行中获取。
有关详细信息,请参阅documentation
答案 3 :(得分:1)
较新版本的 Pandas 还具有保存泡菜的功能。
我觉得这更容易。例如
pd.to_pickle(object_to_save,'/temp/saved_pkl.pickle' )
答案 4 :(得分:0)
使用您的问题中的company1
和python3的快速示例。
import pickle
# Save the file
pickle.dump(company1, file = open("company1.pickle", "wb"))
# Reload the file
company1_reloaded = pickle.load(open("company1.pickle", "rb"))
但是,正如answer所指出的,泡菜经常失败。因此,您应该真正使用dill
。
import dill
# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))
# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))