保存对象(数据持久性)

我创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对象,如上面的示例所示,但是当它们的数量不知名时,通常更容易将它们全部存储在某种不同大小的容器中,比如listtuple或者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是通过引用对象定义来序列化的)。

在所有这些情况下, picklecPickle都可能使你失败。

如果你正在寻找保存一个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来获得最新的发行版本。