首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >保存对象(数据持久化)

保存对象(数据持久化)
EN

Stack Overflow用户
提问于 2010-12-25 17:02:04
回答 5查看 317K关注 0票数 309

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

代码语言:javascript
复制
company1.name = 'banana' 
company1.value = 40

我想要保存此对象。我该怎么做呢?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-12-25 17:35:16

您可以使用标准库中的pickle模块。下面是它在您的示例中的基本应用:

代码语言:javascript
复制
import pickle

class Company(object):
    def __init__(self, name, value):
        self.name = name
        self.value = value

with open('company_data.pkl', 'wb') as outp:
    company1 = Company('banana', 40)
    pickle.dump(company1, outp, pickle.HIGHEST_PROTOCOL)

    company2 = Company('spam', 42)
    pickle.dump(company2, outp, pickle.HIGHEST_PROTOCOL)

del company1
del company2

with open('company_data.pkl', 'rb') as inp:
    company1 = pickle.load(inp)
    print(company1.name)  # -> banana
    print(company1.value)  # -> 40

    company2 = pickle.load(inp)
    print(company2.name) # -> spam
    print(company2.value)  # -> 42

您还可以定义自己的简单实用程序,如下所示,用于打开文件并向其中写入单个对象:

代码语言:javascript
复制
def save_object(obj, filename):
    with open(filename, 'wb') as outp:  # Overwrites any existing file.
        pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')

更新

由于这是一个如此受欢迎的答案,我想触及一些稍微高级的用法主题。

cPickle (或_pickle)与pickle

pickle相比,实际上使用cPickle模块几乎总是更可取的,因为前者是用C编写的,而且速度快得多。它们之间有一些细微的差异,但在大多数情况下,它们是等效的,C版本将提供非常优越的性能。切换到它再简单不过了,只需将import语句改为:

代码语言:javascript
复制
import cPickle as pickle

在Python3中,cPickle被重命名为_pickle,但不再需要这样做,因为pickle模块现在会自动执行此操作-请参阅What difference between pickle and _pickle in python 3?

总结是,您可以使用类似以下内容来确保当C版本在Python 2和3中可用时,您的代码将始终使用C版本:

代码语言:javascript
复制
try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

数据流格式(协议)

pickle可以用几种不同的、Python特定的、称为协议的格式来读写文件,如documentation中所述,“协议版本0”是ASCII码,因此是“人类可读的”。大于0的版本是二进制的,可用的最高版本取决于所使用的Python版本。默认值也取决于Python版本。在Python2中,默认的是协议版本0,但是在Python3.8.1中,它是协议版本4。在Python3.x中,该模块添加了一个pickle.DEFAULT_PROTOCOL,但在Python2中不存在。

幸运的是,在每次调用中都有编写pickle.HIGHEST_PROTOCOL的简写(假设这是您想要的,通常也是这样做的),只需使用文字数字-1 -类似于通过负索引引用序列的最后一个元素。因此,与其写:

代码语言:javascript
复制
pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

你可以这样写:

代码语言:javascript
复制
pickle.dump(obj, outp, -1)

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

代码语言:javascript
复制
pickler = pickle.Pickler(outp, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

注意:如果您在运行不同版本的Python的环境中,那么您可能希望显式地使用(即硬编码)所有版本都可以读取的特定协议号(较高版本通常可以读取较早版本生成的文件)。

多个对象

虽然一个pickle文件可以包含任意数量的pickle对象,如上面的示例所示,但当它们的数量未知时,通常更容易将它们全部存储在某种大小可变的容器中,如listtupledict,并在一个调用中将它们全部写入文件:

代码语言:javascript
复制
tech_companies = [
    Company('Apple', 114.18), Company('Google', 908.60), Company('Microsoft', 69.18)
]
save_object(tech_companies, 'tech_companies.pkl')

并在稍后使用以下命令恢复列表和其中的所有内容:

代码语言:javascript
复制
with open('tech_companies.pkl', 'rb') as inp:
    tech_companies = pickle.load(inp)

主要的优点是您不需要知道保存了多少对象实例,以便稍后加载它们(虽然可以在没有这些信息的情况下执行此操作,但它需要一些稍微特殊的代码)。有关不同方法的详细信息,请参阅相关问题Saving and loading multiple objects in pickle file?的答案。就我个人而言,我最喜欢@Lutz Prechelt的answer,所以这就是下面示例代码中使用的方法:

代码语言:javascript
复制
class Company:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def pickle_loader(filename):
    """ Deserialize a file of pickled objects. """
    with open(filename, "rb") as f:
        while True:
            try:
                yield pickle.load(f)
            except EOFError:
                break

print('Companies in pickle file:')
for company in pickle_loader('company_data.pkl'):
    print('  name: {}, value: {}'.format(company.name, company.value))
票数 575
EN

Stack Overflow用户

发布于 2014-08-04 20:49:23

我认为假设对象是一个class是一个非常有力的假设。如果它不是class呢?还有一个假设是,对象没有在解释器中定义。如果它是在解释器中定义的呢?另外,如果属性是动态添加的呢?当一些python对象在创建后将属性添加到它们的__dict__中时,pickle不会考虑添加这些属性(即,它“忘记”它们是被添加的--因为pickle通过引用对象定义来序列化)。

在所有这些情况下,picklecPickle都会让你大失所望。

如果您希望保存具有属性(在对象定义中添加或之后添加)的object (任意创建的)…最好的选择是使用dill,它可以序列化python中的几乎任何内容。

我们从一个类…开始

代码语言:javascript
复制
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)
... 
>>> 

现在关机,然后重启...

代码语言:javascript
复制
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'
>>> 

Oops…pickle不能处理它。让我们试试dill。为了更好地衡量,我们将抛出另一个对象类型( lambda)。

代码语言:javascript
复制
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)
... 
>>> 

现在读出文件。

代码语言:javascript
复制
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会话,稍后再回来。

代码语言:javascript
复制
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')
>>> 

现在关掉你的电脑,去享受一杯浓缩咖啡或者别的什么,然后再回来...

代码语言:javascript
复制
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获取最新发布的版本。

票数 58
EN

Stack Overflow用户

发布于 2019-11-28 11:26:25

在python3中使用您问题中的company1的一个简单示例。

代码语言:javascript
复制
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所指出的,pickle经常失败。所以你真的应该使用dill

代码语言:javascript
复制
import dill

# Save the file
dill.dump(company1, file = open("company1.pickle", "wb"))

# Reload the file
company1_reloaded = dill.load(open("company1.pickle", "rb"))
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4529815

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档