保存对象(数据持久性)

Pet*_*one 206 python serialization object save pickle

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

company1.name = 'banana' 
company1.value = 40
Run Code Online (Sandbox Code Playgroud)

我想保存这个对象.我怎样才能做到这一点?

mar*_*eau 406

您可以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
Run Code Online (Sandbox Code Playgroud)

您还可以编写一个简单的实用程序,如下所示,它打开一个文件并向其写入一个对象:

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')
Run Code Online (Sandbox Code Playgroud)

更新:

由于这是一个非常受欢迎的答案,我想谈谈一些稍微高级的使用主题.

cPickle(或_pickle)vspickle

实际使用cPickle模块几乎总是优先考虑,而不是pickle因为前者是用C语言编写的,而且速度要快得多.它们之间存在一些细微差别,但在大多数情况下它们是等效的,C版本将提供极大的优越性能.切换到它可能不容易,只需将import语句更改为:

import cPickle as pickle
Run Code Online (Sandbox Code Playgroud)

在Python 3中,cPickle重命名了_pickle,但是由于pickle模块现在自动执行,所以不再需要这样做- 看看python 3中的pickle和_pickle有什么区别?.

您可以使用以下内容来确保您的代码在Python 2和3中都可用时始终使用C版本:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle
Run Code Online (Sandbox Code Playgroud)

数据流格式(协议)

pickle可以用几种不同的,特定于Python的格式(称为协议)来读写文件."协议版本0"是ASCII,因此是"人类可读的".版本> 1是二进制的,可用的最高版本取决于正在使用的Python版本.默认值还取决于Python版本.在Python 2中,默认为Protocol版本0,但在Python 3.6中,它是Protocol版本4.在Python 3.x中,模块pickle.DEFAULT_PROTOCOL添加了一个,但在Python 2中不存在.

幸运的是,pickle.HIGHEST_PROTOCOL在每个调用中都有写入的速记(假设这是你想要的,而且你通常这样做) - 只能使用文字数字-1.所以,而不是写:

pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
Run Code Online (Sandbox Code Playgroud)

你可以写:

pickle.dump(obj, output, -1)
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,如果您创建了一个Pickler用于多个pickle操作的对象,您只需指定一次协议:

pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...
Run Code Online (Sandbox Code Playgroud)

多个对象

虽然泡菜文件可以包含如上述样品中,当有这些数目不详的任何数量的腌制对象的,它往往更容易将其全部保存在某种可变大小的容器,就像一个list,tupledict写它们都是一次调用的文件:

tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')
Run Code Online (Sandbox Code Playgroud)

并使用以下命令恢复列表及其中的所有内容:

with open('tech_companies.pkl', 'rb') as input:
    tech_companies = pickle.load(input)
Run Code Online (Sandbox Code Playgroud)

主要优点是您不需要知道保存了多少对象实例以便稍后加载它们(尽管这样做没有这些信息可能的,它需要一些稍微专门的代码).查看相关问题的答案在pickle文件中保存和加载多个对象?有关不同方法的详细信息.就个人而言,我最喜欢@Lutz Prechelt的答案.这是适应这里的例子:

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))
Run Code Online (Sandbox Code Playgroud)

  • @Peterstone:如果你只想存储一个对象,你只需要一半的代码就像我的例子一样 - 我故意按照我的方式编写它,以显示如何保存多个对象(以后再读回)来自)相同的文件. (4认同)
  • @martinaeau,这是对perstones的回应,关于一个应该只有一个函数将对象保存到磁盘.泡菜责任是_only_将对象转换为可以作为块处理的数据.将文件写入文件是文件对象的责任.通过保持分离,可以实现更高的重用,例如能够通过网络连接发送pickle数据或将其存储在数据库中,所有职责都与实际数据< - >对象转换分开 (3认同)
  • 这对我来说很少见,因为我想象会有一种更简单的方法来保存对象...类似 'saveobject(company1,c:\mypythonobjects) (2认同)
  • @Mike:抱歉,我不认为这个问题是正确的。FWIW,我认为推广“dill”的有效方法是在其下载页面上更清楚地说明它可以做什么而“pickle”不能做什么,而不是建议使用它来解决与当前问题无关的问题所以帖子。如果人们一致认为它充分解决了人们在尝试使用“pickle”时经常遇到的严重缺陷,那么也许它应该成为标准库的一部分。 (2认同)

Mik*_*rns 47

我认为假设对象是一个非常强大的假设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)
... 
>>> 
Run Code Online (Sandbox Code Playgroud)

现在关闭,重启......

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'
>>> 
Run Code Online (Sandbox Code Playgroud)

哎呀... 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)
... 
>>> 
Run Code Online (Sandbox Code Playgroud)

现在阅读文件.

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
>>>    
Run Code Online (Sandbox Code Playgroud)

有用.其原因pickle失败,并且dill不,是,dill治疗__main__等的模块(在大多数情况下),也可以通过参考腌制类定义而不是酸洗(像pickle一样).dill可以腌制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')
>>> 
Run Code Online (Sandbox Code Playgroud)

现在关闭你的电脑,享用浓咖啡或其他什么,然后再回来......

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>
Run Code Online (Sandbox Code Playgroud)

唯一的主要缺点是它dill不是python标准库的一部分.因此,如果您无法在服务器上安装python包,则无法使用它.

但是,如果你能够在系统上安装Python包,你可以得到最新的dillgit+https://github.com/uqfoundation/dill.git@master#egg=dill.你可以获得最新发布的版本pip install dill.

  • 对于跟随的人,这里是[相关问题](http://stackoverflow.com/questions/25538812/python-typeerror-on-load-object-using-dill)@MikeLL发布 - 从答案中,它显然是没有不是'莳萝'问题. (3认同)

Ant*_*ert 9

使用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"))
Run Code Online (Sandbox Code Playgroud)

然而,正如这个答案所指出的,泡菜经常失败。所以你真的应该使用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"))
Run Code Online (Sandbox Code Playgroud)


c0f*_*0de 7

您可以使用anycache来为您完成这项工作。它考虑了所有细节:

  • 它使用dill作为后端,扩展了 pythonpickle模块来处理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)
Run Code Online (Sandbox Code Playgroud)

myfuncAnycache第一次调用并将结果腌制到文件中,cachedir使用唯一标识符(取决于函数名称及其参数)作为文件名。在任何连续运行中,都会加载腌制的对象。如果cachedir在 python 运行之间保留了 ,则 pickled 对象将从之前的 python 运行中获取。

有关更多详细信息,请参阅文档