保存对象(数据持久性)[重复]

2024-12-02 08:41:00
admin
原创
174
摘要:问题描述:我创建了一个这样的对象:company1.name = 'banana' company1.value = 40 我想保存此对象。我该怎么做?解决方案 1:您可以使用pickle标准库中的模块。以下是将其基本应用于您的示例:import pickle class Company(object):...

问题描述:

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

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

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


解决方案 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模块现在会自动执行此操作 - 请参阅python 3 中 pickle 和 _pickle 有什么区别?。

简而言之,您可以使用类似下面的方法确保当C 版本在 Python 2 和 3 中可用时,您的代码将始终使用 C 版本:

try:
    import cPickle as pickle
except ModuleNotFoundError:
    import pickle

数据流格式(协议)

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

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

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

你可以这样写:

pickle.dump(obj, outp, -1)

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

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

注意:如果您处于运行不同版本 Python 的环境中,那么您可能需要明确使用(即硬编码)所有版本都可以读取的特定协议号(更高版本通常可以读取早期版本生成的文件)。

多个对象

虽然 pickle 文件可以包含任意数量的 pickle 对象(如上例所示),但当它们的数量未知时,通常更容易将它们全部存储在某种大小可变的容器中,例如listtuple或 ,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 inp:
    tech_companies = pickle.load(inp)

主要优点是您不需要知道保存了多少个对象实例以便稍后重新加载它们(尽管在没有该信息的情况下也可以这样做但它需要一些稍微专门的代码)。有关执行此操作的不同方法的详细信息,请参阅相关问题“在 pickle 文件中保存和加载多个对象?”的答案。我个人最喜欢@Lutz Prechelt 的答案,所以这是下面示例代码中使用的方法:

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))

解决方案 2:

我认为,假设对象是 是一个相当强的假设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)
... 
>>> 

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

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我们再加一个对象类型 (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)
... 
>>> 

现在读取文件。

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 类定义,而不是通过引用 pickle(像它所做的那样)。可以 pickle 的pickle原因是它给它一个名字……然后 pickle 魔法就会发生。dill`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')
>>> 

现在关闭您的计算机,去享用一杯浓咖啡或任何其他东西,然后稍后再回来......

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 包,你可以使用 获取最新版本dillgit+https://github.com/uqfoundation/dill.git@master#egg=dill并且你可以使用 获取最新发布的版本pip install dill

解决方案 3:

使用您的问题的快速示例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"))

但是,正如这个答案所指出的,pickle 经常会失败。所以你真的应该使用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"))

解决方案 4:

你可以使用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)

Anycachemyfunc首次调用时cachedir,使用唯一标识符(取决于函数名称及其参数)作为文件名将结果 pickle 到文件中。在任何连续运行中,都会加载 pickle 对象。如果cachedir在 python 运行之间保留,则 pickle 对象取自上一次 python 运行。

更多详细信息请参阅文档

解决方案 5:

较新版本的 pandas 还具有保存 pickle 的功能。

我觉得这样更容易。例如

pd.to_pickle(object_to_save,'/temp/saved_pkl.pickle' )

解决方案 6:

虽然pickle这是序列化对象最广泛使用的选项,但它并非没有问题,尤其是在安全性方面。可以构造一个 Python 对象,当该对象反序列化时,它将执行任意代码。下面提供了一个例子。

import pickle

class Example:
    def __reduce__(self):
        print("Serialised")
        return print, ("Deserialised",)

example = Example()

serialised = pickle.dumps(example) # prints "Serialised"
pickle.loads(serialised) # prints "Deserialised"

虽然上面的例子完全无害,但可以很容易地使它变得更加糟糕,例如通过在返回值中print替换exec或。eval

对此并没有真正好的解决方案,但人们提到dill库是 的可能替代方案pickle。但是,还有另一种替代方案。marshal它是一个内置序列化库,但它的缺点是它不适用于某些类型的对象(尽管与 不同pickle,它可以序列化 CodeObjects,这就是为什么它在将 CodeObjects 编译为文件期间在内部用于序列化上述内容.pyc)。它也更快,用 C 编写(代码在这里),但尚未经过严格审核。但是,没有已知的安全漏洞。

marshal实际上是 的替代品pickleloadsload和方法dumpsdump做同样的事情。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2079  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1459  
  建筑行业正处于数字化转型的关键时期,建筑产品生命周期管理(PLM)系统的实施对于提升项目效率、质量和协同性至关重要。特别是在 2025 年,基于建筑信息模型(BIM)的项目进度优化工具成为众多建筑企业关注的焦点。这些工具不仅能够整合项目全生命周期的数据,还能通过精准的分析和模拟,为项目进度管理提供强大支持。BIM 与建...
plm是什么软件   0  
  PLM系统开发的重要性与现状PLM(产品生命周期管理)系统在现代企业的产品研发、生产与管理过程中扮演着至关重要的角色。它贯穿产品从概念设计到退役的整个生命周期,整合了产品数据、流程以及人员等多方面的资源,极大地提高了企业的协同效率和创新能力。通过PLM系统,企业能够实现产品信息的集中管理与共享,不同部门之间可以实时获取...
国产plm软件   0  
  PLM(产品生命周期管理)系统在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和技术的飞速发展,企业对PLM系统的迭代周期优化需求日益迫切。2025年敏捷认证对项目管理提出了新的要求,其中燃尽图作为一种强大的可视化工具,在PLM系统迭代周期优化中有着广泛且重要的应用。深入探讨这些应用,对于提升企业的项...
plm系统主要干什么的   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用