小编典典

保存对象(数据持久性)

all

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

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

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


阅读 107

收藏
2022-04-12

共1个答案

小编典典

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

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

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

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

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

import cPickle as pickle

在 Python 3 中,cPickle已重命名_pickle,但不再需要这样做,因为pickle模块现在会自动执行- 看看 python3 中的 pickle 和 _pickle有什么区别?.

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

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

数据流格式(协议)

pickle可以读取和写入几种不同的、特定于 Python
的格式的文件,称为文档中描述的 协议 ,“协议版本 0”是 ASCII,因此是“人类可读的”。版本 > 0 是二进制的,可用的最高版本取决于使用的 Python
版本。默认值还取决于 Python 版本。在 Python 2 中,默认值为 Protocol version ,但在 Python 3.8.1 中,它是
Protocol version 。在 Python 3.x 中,该模块添加了一个,但在 Python 2
中不存在。0``4``pickle.DEFAULT_PROTOCOL

幸运的是,每次调用都有简写pickle.HIGHEST_PROTOCOL(假设这是您想要的,并且您通常会这样做),只需使用文字数字-1——类似于通过负索引引用序列的最后一个元素。所以,而不是写:

pickle.dump(obj, outp, pickle.HIGHEST_PROTOCOL)

你可以写:

pickle.dump(obj, outp, -1)

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

pickler = pickle.Pickler(outp, -1)
pickler.dump(obj1)
pickler.dump(obj2)
   etc...

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

多个对象

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

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 inp:
    tech_companies = pickle.load(inp)

主要优点是您不需要知道保存了多少对象实例以便稍后加载它们(尽管在没有这些信息的情况下这样做
可能的,但它需要一些稍微专门的代码)。就我个人而言,这就是下面示例代码中使用的方法:

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))
2022-04-12