Python 保存对象(数据持久性)

Python 保存对象(数据持久性),python,object,serialization,save,pickle,Python,Object,Serialization,Save,Pickle,我创建了这样一个对象: company1.name = 'banana' company1.value = 40 我想保存这个对象。我该怎么做呢?您可以使用标准库中的pickle模块。 下面是它在您的示例中的一个基本应用: import pickle class Company(object): def __init__(self, name, value): self.name = name self.value = value with ope

我创建了这样一个对象:

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:  # Overwrites any existing file.
        pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)

# sample usage
save_object(company1, 'company1.pkl')
更新 由于这是一个非常流行的答案,我想谈谈一些稍微高级的用法主题

cPickle
(或
\u pickle
)与
pickle
实际上最好使用模块而不是pickle,因为前者是用C编写的,速度更快。它们之间有一些细微的区别,但在大多数情况下它们是等效的,C版本将提供非常优异的性能。切换到它非常简单,只需将
import
语句更改为:

import cPickle as pickle
在Python3中,
cPickle
被重命名为
\u pickle
,但是这样做不再必要,因为
pickle
模块现在会自动看到

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

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle
数据流格式(协议)
pickle
可以读写几种不同的、特定于Python的格式的文件,称为协议,如中所述,“协议版本0”是ASCII,因此是“人类可读的”。版本>0是二进制的,可用的最高版本取决于所使用的Python版本。默认值还取决于Python版本。在Python2中,默认为协议版本
0
,但在Python3.8.1中,它是协议版本
4
。在Python3.x中,该模块添加了
pickle.DEFAULT\u协议,但在Python2中不存在该协议

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

pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
你可以写:

pickle.dump(obj, output, -1)
无论哪种方式,如果您创建了一个用于多个pickle操作的
Pickler
对象,则只需指定一次协议:

pickler = pickle.Pickler(output, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...
注意:如果您所处的环境运行的是不同版本的Python,那么您可能希望显式地使用(即硬代码)所有人都可以读取的特定协议号(较新版本通常可以读取早期版本生成的文件)

多个对象 虽然pickle文件可以包含任意数量的pickle对象,如上面的示例所示,但当存在未知数量的pickle对象时,通常更容易将它们全部存储在某种大小可变的容器中,如
列表
元组
、或
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)
主要的优点是,您不需要知道保存了多少对象实例才能在以后加载它们(尽管在没有这些信息的情况下这样做是可能的,但它需要一些稍微专门化的代码)。请参阅相关问题的答案,以了解执行此操作的不同方法的详细信息。我个人认为@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))

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

在所有这些情况下,
pickle
cPickle
都会让你非常失败

如果您希望保存一个
对象(任意创建),其中包含属性(添加到对象定义中或之后)…最好使用
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'
>>> 
现在读取文件

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
>>>    
现在关闭你的电脑,去喝一杯浓缩咖啡或其他什么,然后回来

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>
Python 2.7.8(默认,2014年7月13日02:29:54)
达尔文的[GCC 4.2.1兼容Apple Clang 4.1((tags/Apple/Clang-421.11.66))]
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>进口莳萝
>>>dill.load_会话(“dill.pkl”)
>>>公司名称
“香蕉”
>>>公司价值
40
>>>公司名称
“大黄”
>>>公司价值
42
>>>公司2
唯一的主要缺点是,
dill
不是python标准库的一部分。因此,如果不能在服务器上安装python包,那么就不能使用它

但是,如果您能够在系统上安装python软件包,则可以使用
git获得最新的
dill
+https://github.com/uqfoundation/dill.git@master#egg=dill
。您可以使用
pip-install-dill
获得最新发布的版本,您可以使用它来完成这项工作。它考虑了所有细节:

  • 它用作后端, 它扩展了python
    pickle
    模块来处理
    lambda
    和所有的nice python特性
  • 它将不同的对象存储到不同的文件中,并正确地重新加载它们
  • 限制缓存大小
  • 允许缓存清除
  • 允许在多个运行之间共享对象
  • 允许考虑影响结果的输入文件
假设您有一个函数
myfunc
wh
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>
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)
pd.to_pickle(object_to_save,'/temp/saved_pkl.pickle' )