保存对象(数据持久性)
我创build了一个这样的对象:
company1.name = 'banana' company1.value = 40
我想保存这个对象。 我怎样才能做到这一点?
你可以在标准库中使用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: pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL) # sample usage save_object(company1, 'company1.pkl')
更新:
由于这是一个非常受欢迎的答案,所以我想介绍一些稍微高级的使用主题。
cPickle
vs pickle
实际上使用cPickle
模块,而不是pickle
总是比较好,因为前者是用C编写的,速度要快得多。 它们之间有一些细微的差别,但是在大多数情况下它们是相同的,而C版本将会提供更好的性能。 切换到它并不容易,只要将import
语句改为:
import cPickle as pickle
在Python 3中, cPickle
被重命名为_pickle
,但是现在不再需_pickle
,因为pickle
模块现在可以自动完成了 – 请参阅python 3中pickle和_pickle之间的区别? 。
数据stream格式(协议)
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)
无论哪种方式,如果您创buildPickler
对象以在多个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)
主要优点是你不需要知道有多less对象实例被保存,以便稍后加载它们(尽pipe没有这些信息是可能的,它需要一些专门的代码)。 查看相关问题的答案在pickle文件中保存和加载多个对象? 关于如何的细节。
我认为假设对象是一个class
是一个非常强大的假设。 如果不是class
呢? 还有一个假设,即在解释器中没有定义对象。 如果在翻译中有定义呢? 另外,如果属性是dynamic添加的呢? 当一些python对象在创build之后添加了属性__dict__
, pickle
并不考虑添加这些属性(即它们被添加 – 因为pickle
是通过引用对象定义来序列化的)。
在所有这些情况下, pickle
和cPickle
都可能使你失败。
如果你正在寻找保存一个object
(任意创build),你有属性(要么添加到对象定义,要么之后)……最好的办法是使用dill
,它可以序列化几乎任何东西在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 >>> 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) ... >>>
现在closures,然后重新启动…
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
。 我们会抛出另一个对象types( 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类定义,而不是pickling pickling(像pickle
一样)。 dill
可以腌制一个lambda
的原因是,它给它一个名字…然后酸洗魔术可能会发生。
实际上,有一个更简单的方法来保存所有这些对象,特别是如果你有很多创build的对象。 只需转储整个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 >>> 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') >>>
现在closures你的电脑,享受一杯咖啡或其他什么,然后回来…
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
不是Python标准库的一部分。 所以,如果你不能在你的服务器上安装python包,那么你不能使用它。
但是,如果您能够在您的系统上安装python软件包,则可以使用git+https://github.com/uqfoundation/dill.git@master#egg=dill
获取最新的dill
。 你可以用pip install dill
来获得最新的发行版本。