保存对象(数据持久性)

时间:2010-12-25 09:02:04

标签: python object serialization save pickle

我创建了一个这样的对象:

company1.name = 'banana' 
company1.value = 40

我想保存这个对象。我怎么能这样做?

5 个答案:

答案 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对象,如上面的示例所示,当它们的数目未知时,通常更容易将它们存储在某种变化中 - 大小的容器,如listtupledict,并在一次调用中将它们全部写入文件:

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序列化了引用对象定义)。

在所有这些情况下,cPickleobject可能会让您失望。

如果您要保存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为您完成这项工作。它考虑了所有细节:

  • 它使用dill作为后端, 它扩展了python 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"))