我创建了一个像这样的对象:
company1.name = 'banana'
company1.value = 40
我想保存这个对象。我怎样才能做到这一点?
原文由 Peterstone 发布,翻译遵循 CC BY-SA 4.0 许可协议
我创建了一个像这样的对象:
company1.name = 'banana'
company1.value = 40
我想保存这个对象。我怎样才能做到这一点?
原文由 Peterstone 发布,翻译遵循 CC BY-SA 4.0 许可协议
我认为假设对象是 class
是一个非常有力的假设。如果它不是 class
怎么办?还有一个假设是该对象未在解释器中定义。如果它是在解释器中定义的怎么办?另外,如果属性是动态添加的呢?当某些 python 对象将属性添加到它们的 __dict__
创建后, pickle
不尊重这些属性的添加(即它“忘记”它们被添加了 - 因为 pickle
通过引用对象定义进行序列化)。
在所有这些情况下, pickle
和 cPickle
你非常失望。
如果你想保存一个 object
(任意创建),你有属性(在对象定义中添加,或者之后添加)……你最好的选择是使用 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)
...
>>>
现在关机,重启…
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
。我们将放入另一种对象类型 (a 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
>>>
有用。 The reason pickle
fails, and dill
doesn’t, is that dill
treats __main__
like a module (for the most part),并且还可以腌制类定义而不是通过引用腌制(就像 pickle
一样)。 dill
可以 pickle a lambda
的原因是它给了它一个名字……然后酸洗魔法就会发生。
实际上,有一种更简单的方法来保存所有这些对象,尤其是当您创建了很多对象时。只需转储整个 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')
>>>
现在关掉你的电脑,去喝杯浓缩咖啡之类的,然后再回来……
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 包,您可以获得最新的 dill
和 git+https://github.com/uqfoundation/dill.git@master#egg=dill
。您可以使用 pip install dill
获取最新发布的版本。
原文由 Mike McKerns 发布,翻译遵循 CC BY-SA 3.0 许可协议
4 回答4.4k 阅读✓ 已解决
4 回答3.8k 阅读✓ 已解决
1 回答3k 阅读✓ 已解决
3 回答2.1k 阅读✓ 已解决
1 回答4.4k 阅读✓ 已解决
1 回答3.8k 阅读✓ 已解决
1 回答2.8k 阅读✓ 已解决
您可以使用标准库中的
pickle
模块。这是它在您的示例中的基本应用:您还可以定义自己的简单实用程序,如下所示,它打开文件并向其中写入单个对象:
更新
由于这是一个如此受欢迎的答案,我想谈谈一些稍微高级的使用主题。
cPickle
(或_pickle
)与pickle
实际使用
cPickle
模块而不是pickle
几乎总是更可取,因为前者是用 C 编写的,而且速度更快。它们之间有一些细微的差别,但在大多数情况下它们是等价的,C 版本将提供非常出色的性能。切换到它再简单不过了,只需将import
语句更改为:在 Python 3 中,
cPickle
被重命名为_pickle
,但是不再需要这样做了,因为pickle
模块现在自动执行它 - 看看 pickle 和 _pickle 之间有什么区别在 python 3 中? .简而言之,您可以使用以下内容来确保您的代码在 Python 2 和 3 中都可用时 始终 使用 C 版本:
数据流格式(协议)
pickle
可以读取和写入几种不同的、特定于 Python 的格式的文件,称为 文档 中描述的 _协议_,“协议版本 0”是 ASCII,因此是“人类可读的”。版本 > 0 是二进制的,可用的最高版本取决于使用的 Python 版本。默认值还取决于 Python 版本。在 Python 2 中,默认值为 Protocol version0
,但在 Python 3.8.1 中,它是 Protocol version4
。在 Python 3.x 中,该模块添加了pickle.DEFAULT_PROTOCOL
,但在 Python 2 中不存在。幸运的是,在每次调用中都有写
pickle.HIGHEST_PROTOCOL
的简写(假设这是您想要的,而且您通常会这样做),只需使用文字数字-1
— 类似于引用序列的最后一个元素通过负指数。所以,而不是写:你可以写:
无论哪种方式,如果您创建了
Pickler
用于多个泡菜操作的对象,则只需指定一次协议:_注意_:如果您在运行不同版本 Python 的环境中,那么您可能希望显式使用(即硬编码)所有它们都可以读取的特定协议号(更高版本通常可以读取由早期版本生成的文件) .
多个对象
虽然泡菜文件 可以 包含任意数量的泡菜对象,如上面的示例所示,但当它们的数量未知时,将它们全部存储在某种可变大小的容器中通常更容易,例如
list
,tuple
或dict
并在一次调用中将它们全部写入文件:并稍后恢复列表及其中的所有内容:
主要优点是您不需要知道保存了多少对象实例以便稍后加载它们(尽管在没有这些信息的情况下这样做 是 可能的,但它需要一些稍微专门的代码)。查看相关问题的答案 在泡菜文件中保存和加载多个对象? 有关执行此操作的不同方法的详细信息。就我个人而言,我最喜欢@Lutz Prechelt 的 回答,所以这就是下面示例代码中使用的方法: