33、Python数据持久化与序列化方法详解

Python数据持久化与序列化方法详解

在系统管理工作中,构建GUI应用程序虽并非传统职责,但它是一项极具价值的技能。有时,你可能需要为用户创建简单应用;有时则是给自己用;还有时,虽非必需,但它能让某些任务执行得更顺畅。当你熟练掌握构建GUI应用的技巧后,会惊讶地发现自己频繁用到这项能力。

而在数据处理方面,数据持久化是一个重要概念。简单来说,数据持久化是指将数据保存起来以供后续使用,即使保存数据的进程终止,数据依然存在。通常,这是通过将数据转换为某种格式并写入磁盘来实现的。数据格式有人类可读的,如XML或YAML;也有人类无法直接使用的,如Berkeley DB文件(bdb)或SQLite数据库。

1. 数据持久化的应用场景

以下是一些需要保存数据以供后续使用的场景:
- 文件修改跟踪脚本 :你有一个脚本用于跟踪目录中文件的最后修改日期,需要偶尔运行该脚本来查看自上次运行后哪些文件发生了变化。此时,文件的相关数据就需要保存下来,下次运行脚本时使用。
- 网络性能监测脚本 :有一台机器存在潜在网络问题,你决定每15分钟运行一次脚本,查看它与网络中其他多台机器的ping响应时间。这些ping时间数据可以保存到持久化数据文件中,以便后续分析。

2. 简单序列化方法

序列化是将数据存储到磁盘以供后续使用的过程,这里先介绍简单序列化,即保存数据到磁盘时不保存数据之间的关系。

2.1 Pickle模块

Pickle是Python标准库中的一个模块,是最基本的简单序列化机制。从农业或烹饪的角度理解,“pickling”就像是将食物保存起来放入罐中,日后再使用。在Python中,使用Pickle模块可以将对象写入磁盘,退出Python进程,之后再重新启动进程,从磁盘读取对象并与之交互。

可Pickle的对象类型
- None、true和false
- 整数、长整数、浮点数、复数
- 普通字符串和Unicode字符串
- 仅包含可Pickle对象的元组、列表、集合和字典
- 在模块顶层定义的函数
- 在模块顶层定义的内置函数
- 在模块顶层定义的类
- 其 __dict__ __setstate__() 可Pickle的此类类的实例

序列化对象到磁盘

In [1]: import pickle
In [2]: some_dict = {'a': 1, 'b': 2}
In [3]: pickle_file = open('some_dict.pkl', 'w')
In [4]: pickle.dump(some_dict, pickle_file)
In [5]: pickle_file.close()

反序列化Pickle文件

In [1]: import pickle
In [2]: pickle_file = open('some_dict.pkl', 'r')
In [3]: another_name_for_some_dict = pickle.load(pickle_file)
In [4]: another_name_for_some_dict
Out[4]: {'a': 1, 'b': 2}

可以将多个对象存储到一个Pickle文件中:

In [1]: list_of_dicts = [{str(i): i} for i in range(5)]
In [2]: list_of_dicts
Out[2]: [{'0': 0}, {'1': 1}, {'2': 2}, {'3': 3}, {'4': 4}]
In [3]: import pickle
In [4]: pickle_file = open('list_of_dicts.pkl', 'w')
In [5]: for d in list_of_dicts:
   ...:     pickle.dump(d, pickle_file)
   ...:     
   ...:     
In [6]: pickle_file.close()

反序列化包含多个对象的Pickle文件:

In [1]: import pickle
In [2]: pickle_file = open('list_of_dicts.pkl', 'r')
In [3]: while 1:
   ...:     try:
   ...:         print pickle.load(pickle_file)
   ...:     except EOFError:
   ...:         print "EOF Error"
   ...:         break
   ...:         
   ...:         
{'0': 0}
{'1': 1}
{'2': 2}
{'3': 3}
{'4': 4}
EOF Error

Pickle还可以处理自定义类的对象。首先定义一个自定义类:

#!/usr/bin/env python
class MyClass(object):
    def __init__(self):
        self.data = []
    def __str__(self):
        return "Custom Class MyClass Data:: %s" % str(self.data)
    def add_item(self, item):
        self.data.append(item)

将自定义类的对象进行Pickle序列化:

#!/usr/bin/env python
import pickle
import custom_class
my_obj = custom_class.MyClass()
my_obj.add_item(1)
my_obj.add_item(2)
my_obj.add_item(3)
pickle_file = open('custom_class.pkl', 'w')
pickle.dump(my_obj, pickle_file)
pickle_file.close()

反序列化自定义类的Pickle文件:

#!/usr/bin/env python
import pickle
import custom_class
pickle_file = open('custom_class.pkl', 'r')
my_obj = pickle.load(pickle_file)
print my_obj
pickle_file.close()

Pickle默认使用一种接近人类可读的协议进行序列化,也可以选择二进制协议。二进制协议在处理对象序列化时间较长时可能更合适:

In [1]: import pickle
In [2]: default_pickle_file = open('default.pkl', 'w')
In [3]: binary_pickle_file = open('binary.pkl', 'wb')
In [4]: d = {'a': 1}
In [5]: pickle.dump(d, default_pickle_file)
In [6]: pickle.dump(d, binary_pickle_file, -1)
In [7]: default_pickle_file.close()
In [8]: binary_pickle_file.close()
2.2 cPickle模块

在Python标准库中,还有一个 cPickle 模块,它是用C语言实现的。如果发现对象的Pickle序列化过程耗时较长,可以考虑使用 cPickle 模块,其语法与 pickle 模块相同。

2.3 shelve模块

shelve 模块提供了一个简单易用的对象持久化接口,简化了多个对象的持久化操作。可以将多个对象存储在同一个持久化对象存储中,并轻松取回。使用 shelve 存储对象类似于使用Python字典。

示例代码

In [1]: import shelve
In [2]: d = shelve.open('example.s')
In [3]: d
Out[3]: {}
In [4]: d['key'] = 'some value'
In [5]: d.close()
In [6]: d2 = shelve.open('example.s')
In [7]: d2
Out[7]: {'key': 'some value'}

使用 shelve 时需要注意两个问题:
- 关闭对象 :操作完成后必须调用 close() 方法,否则对 shelve 对象所做的更改将不会被持久化。

# 未关闭对象导致数据丢失示例
In [1]: import shelve
In [2]: d = shelve.open('lossy.s')
In [3]: d['key'] = 'this is a key that will persist'
In [4]: d
Out[4]: {'key': 'this is a key that will persist'}
In [5]: d.close()

# 再次打开,添加新项但不关闭
In [1]: import shelve
In [2]: d = shelve.open('lossy.s')
In [3]: d
Out[3]: {'key': 'this is a key that will persist'}
In [4]: d['another_key'] = 'this is an entry that will not persist'
# 直接退出,未调用d.close()

# 再次打开查看
In [1]: import shelve
In [2]: d = shelve.open('lossy.s')
In [3]: d
Out[3]: {'key': 'this is a key that will persist'}
  • 可变对象的修改 :默认情况下,对持久化对象的内联更改不会被捕获。可以通过两种方法解决:
  • 特定针对性方法 :重新赋值给 shelve 对象。
In [1]: import shelve
In [2]: d = shelve.open('mutable_nonlossy.s')
In [3]: d['key'] = []
In [4]: temp_list = d['key']
In [5]: temp_list.append(1)
In [6]: d['key'] = temp_list
In [7]: d.close()
  • 通用的全面方法 :设置 writeback 标志为 True 。但这样会将访问的对象缓存到内存中,在调用 close() 时再进行持久化,可能会增加内存使用和文件同步时间。
In [1]: import shelve
In [2]: d = shelve.open('mutable_nonlossy.s', writeback=True)
In [3]: d['key'] = []
In [4]: d['key'].append(1)
In [5]: d.close()
3. YAML模块

YAML全称可能是“YAML ain’t markup language”或“yet another markup language”,是一种常用于以纯文本布局存储、检索和更新数据的格式,数据通常具有层次结构。

在Python中使用YAML,可先通过 easy_install PyYAML 进行安装。选择YAML而非内置的 pickle 有两个吸引人的原因:
- 人类可读 :语法类似于配置文件,在编辑配置文件是个不错选择的场景下,YAML可能是个好的选择。
- 多语言支持 :YAML解析器已在许多其他语言中实现,如果你需要在Python应用程序和其他语言编写的应用程序之间传输数据,YAML可以作为一个很好的中间解决方案。

序列化示例

In [1]: import yaml
In [2]: yaml_file = open('test.yaml', 'w')
In [3]: d = {'foo': 'a', 'bar': 'b', 'bam': [1, 2,3]}
In [4]: yaml.dump(d, yaml_file, default_flow_style=False)
In [5]: yaml_file.close()

生成的YAML文件内容如下:

bam:
- 1
- 2
- 3
bar: b
foo: a

反序列化示例

In [1]: import yaml
In [2]: yaml_file = open('test.yaml', 'r')
In [3]: yaml.load(yaml_file)
Out[3]: {'bam': [1, 2, 3], 'bar': 'b', 'foo': 'a'}

YAML还支持非块模式的序列化:

In [1]: import yaml
In [2]: yaml_file = open('nonblock.yaml', 'w')
In [3]: d = {'foo': 'a', 'bar': 'b', 'bam': [1, 2,3]}
In [4]: yaml.dump(d, yaml_file)
In [5]: yaml_file.close()

生成的非块模式YAML文件内容如下:

bam: [1, 2, 3] bar: b foo: a

YAML在处理自定义类时的行为与 pickle 类似。以下是序列化和反序列化自定义类对象的示例:

序列化自定义类对象

#!/usr/bin/env python
import yaml
import custom_class
my_obj = custom_class.MyClass()
my_obj.add_item(1)
my_obj.add_item(2)
my_obj.add_item(3)
yaml_file = open('custom_class.yaml', 'w')
yaml.dump(my_obj, yaml_file)
yaml_file.close()

反序列化自定义类对象

#!/usr/bin/env python
import yaml
import custom_class
yaml_file = open('custom_class.yaml', 'r')
my_obj = yaml.load(yaml_file)
print my_obj
yaml_file.close()

总结

本文详细介绍了Python中数据持久化和简单序列化的多种方法,包括 pickle cPickle shelve YAML 。每种方法都有其特点和适用场景, pickle 是基本的序列化模块, cPickle 性能更优, shelve 简化了多对象持久化, YAML 具有人类可读和多语言支持的优势。在实际应用中,需要根据具体需求选择合适的方法。

以下是这些序列化方法的比较表格:
| 方法 | 特点 | 适用场景 |
| ---- | ---- | ---- |
| pickle | 基本的序列化模块,支持自定义类对象,默认协议接近人类可读 | 简单对象的序列化和反序列化,对性能要求不高 |
| cPickle | 用C语言实现,性能优于pickle | 对象序列化耗时较长的情况 |
| shelve | 提供简单易用的多对象持久化接口,类似字典操作 | 需要存储多个对象并方便取回的场景 |
| YAML | 人类可读,多语言支持,数据通常具有层次结构 | 需要在不同语言应用程序间传输数据,或数据需要人工编辑的场景 |

通过合理运用这些方法,可以更好地实现数据的持久化存储和管理。

以下是使用 pickle 模块进行对象序列化和反序列化的流程图:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B(导入pickle模块):::process
    B --> C(创建要序列化的对象):::process
    C --> D(打开文件以写入模式):::process
    D --> E(使用pickle.dump()序列化对象到文件):::process
    E --> F(关闭文件):::process
    F --> G{是否需要反序列化?}:::decision
    G -->|是| H(打开文件以读取模式):::process
    H --> I(使用pickle.load()从文件中反序列化对象):::process
    I --> J(使用反序列化后的对象):::process
    J --> K([结束]):::startend
    G -->|否| K([结束]):::startend

这个流程图展示了使用 pickle 模块进行对象序列化和反序列化的基本流程。首先,导入 pickle 模块,创建要序列化的对象,然后将对象写入文件。接着,根据是否需要反序列化,决定是否从文件中读取对象。如果需要反序列化,将读取文件并使用反序列化后的对象;如果不需要,则直接结束流程。这个流程清晰地展示了 pickle 模块的使用步骤。

Python数据持久化与序列化方法详解

在之前的内容中,我们已经对Python数据持久化的多种简单序列化方法进行了详细介绍。接下来,我们将进一步探讨这些方法在实际应用中的注意事项和一些拓展内容,以及对不同序列化方法的性能对比分析。

4. 序列化方法实际应用注意事项
4.1 Pickle模块的安全性问题

虽然 pickle 模块非常方便,但在使用时需要注意安全性问题。由于 pickle 可以反序列化任意对象,当从不可信的源接收 pickle 数据时,可能会执行恶意代码。例如,如果一个攻击者能够控制 pickle 文件的内容,他们可以构造一个恶意对象,在反序列化时执行任意代码。因此,在实际应用中,不要从不可信的源加载 pickle 数据。

4.2 shelve模块的文件锁定问题

shelve 模块在多进程或多线程环境下使用时,可能会出现文件锁定问题。当多个进程或线程同时尝试访问和修改同一个 shelve 文件时,可能会导致数据不一致或文件损坏。为了避免这个问题,可以使用锁机制来确保同一时间只有一个进程或线程可以访问 shelve 文件。

import shelve
import threading

# 创建一个锁对象
lock = threading.Lock()

def update_shelve():
    with lock:
        d = shelve.open('example.s')
        try:
            if 'counter' not in d:
                d['counter'] = 0
            d['counter'] += 1
        finally:
            d.close()

# 创建多个线程来更新shelve文件
threads = []
for _ in range(10):
    t = threading.Thread(target=update_shelve)
    threads.append(t)
    t.start()

# 等待所有线程完成
for t in threads:
    t.join()

# 查看最终结果
d = shelve.open('example.s')
print(d['counter'])
d.close()
4.3 YAML模块的性能问题

虽然YAML具有人类可读和多语言支持的优点,但在处理大量数据或对性能要求较高的场景下,YAML的序列化和反序列化速度可能较慢。这是因为YAML需要解析和生成文本格式的数据,相比二进制格式的序列化方法,性能会有所损失。在这种情况下,可以考虑使用其他性能更高的序列化方法,如 cPickle

5. 序列化方法性能对比分析

为了更直观地了解不同序列化方法的性能差异,我们可以进行一个简单的性能测试。以下是一个测试代码示例,用于比较 pickle cPickle YAML 在序列化和反序列化一个大型字典时的性能。

import pickle
import cPickle
import yaml
import time

# 创建一个大型字典
large_dict = {str(i): i for i in range(10000)}

# 测试pickle的性能
start_time = time.time()
pickle_file = open('pickle_test.pkl', 'w')
pickle.dump(large_dict, pickle_file)
pickle_file.close()
pickle_file = open('pickle_test.pkl', 'r')
loaded_dict_pickle = pickle.load(pickle_file)
pickle_file.close()
pickle_time = time.time() - start_time

# 测试cPickle的性能
start_time = time.time()
cPickle_file = open('cPickle_test.pkl', 'wb')
cPickle.dump(large_dict, cPickle_file)
cPickle_file.close()
cPickle_file = open('cPickle_test.pkl', 'rb')
loaded_dict_cPickle = cPickle.load(cPickle_file)
cPickle_file.close()
cPickle_time = time.time() - start_time

# 测试YAML的性能
start_time = time.time()
yaml_file = open('yaml_test.yaml', 'w')
yaml.dump(large_dict, yaml_file)
yaml_file.close()
yaml_file = open('yaml_test.yaml', 'r')
loaded_dict_yaml = yaml.load(yaml_file)
yaml_file.close()
yaml_time = time.time() - start_time

# 输出性能测试结果
print(f"Pickle序列化和反序列化时间: {pickle_time} 秒")
print(f"cPickle序列化和反序列化时间: {cPickle_time} 秒")
print(f"YAML序列化和反序列化时间: {yaml_time} 秒")

以下是根据上述测试代码可能得到的性能对比表格:
| 方法 | 序列化和反序列化时间(秒) |
| ---- | ---- |
| pickle | 0.123 |
| cPickle | 0.034 |
| YAML | 0.567 |

从表格中可以看出, cPickle 的性能明显优于 pickle YAML 。这是因为 cPickle 是用C语言实现的,执行速度更快。而 YAML 由于需要处理文本格式的数据,性能相对较差。

6. 序列化方法的选择建议

根据不同的应用场景和需求,可以按照以下建议选择合适的序列化方法:
- 简单对象且对性能要求不高 :可以使用 pickle 模块,它是Python标准库中的基本序列化方法,支持自定义类对象,使用方便。
- 对性能要求较高 :优先选择 cPickle 模块,它的性能明显优于 pickle ,尤其在处理大量数据时更加显著。
- 需要存储多个对象并方便取回 :使用 shelve 模块,它提供了类似字典的操作接口,简化了多对象的持久化操作。
- 需要在不同语言应用程序间传输数据或数据需要人工编辑 :选择 YAML 模块,它具有人类可读和多语言支持的优点。

总结

本文全面介绍了Python中数据持久化和简单序列化的多种方法,包括 pickle cPickle shelve YAML 。详细阐述了它们的使用方法、特点、适用场景以及在实际应用中需要注意的问题。通过性能对比分析,我们可以更清楚地了解不同方法的性能差异,从而在实际开发中根据具体需求选择最合适的序列化方法。

以下是不同序列化方法选择的决策流程图:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{是否需要多语言支持?}:::decision
    B -->|是| C(YAML):::process
    B -->|否| D{是否对性能要求高?}:::decision
    D -->|是| E(cPickle):::process
    D -->|否| F{是否需要存储多个对象?}:::decision
    F -->|是| G(shelve):::process
    F -->|否| H(pickle):::process
    C --> I([结束]):::startend
    E --> I([结束]):::startend
    G --> I([结束]):::startend
    H --> I([结束]):::startend

这个流程图可以帮助我们在面对不同的需求时,快速选择合适的序列化方法。通过合理运用这些序列化方法,我们可以更好地实现数据的持久化存储和管理,提高程序的性能和可维护性。

这个是完整源码 python实现 Flask,Vue 【python毕业设计】基于Python的Flask+Vue物业管理系统 源码+论文+sql脚本 完整版 数据库是mysql 本文首先实现了基于Python的Flask+Vue物业管理系统技术的发展随后依照传统的软件开发流程,最先为系统挑选适用的言语和软件开发平台,依据需求分析开展控制模块制做和数据库查询构造设计,随后依据系统整体功能模块的设计,制作系统的功能模块图、E-R图。随后,设计框架,依据设计的框架撰写编码,完成系统的每个功能模块。最终,对基本系统开展了检测,包含软件性能测试、单元测试和性能指标。测试结果表明,该系统能够实现所需的功能,运行状况尚可并无明显缺点。本文首先实现了基于Python的Flask+Vue物业管理系统技术的发展随后依照传统的软件开发流程,最先为系统挑选适用的言语和软件开发平台,依据需求分析开展控制模块制做和数据库查询构造设计,随后依据系统整体功能模块的设计,制作系统的功能模块图、E-R图。随后,设计框架,依据设计的框架撰写编码,完成系统的每个功能模块。最终,对基本系统开展了检测,包含软件性能测试、单元测试和性能指标。测试结果表明,该系统能够实现所需的功能,运行状况尚可并无明显缺点。本文首先实现了基于Python的Flask+Vue物业管理系统技术的发展随后依照传统的软件开发流程,最先为系统挑选适用的言语和软件开发平台,依据需求分析开展控制模块制做和数据库查询构造设计,随后依据系统整体功能模块的设计,制作系统的功能模块图、E-R图。随后,设计框架,依据设计的框架撰写编码,完成系统的每个功能模块。最终,对基本系统开展了检测,包含软件性能测试、单元测试和性能指标。测试结果表明,该系统能够实现所需的功能,运行状况尚可并无明显缺点。本文首先实现了基于Python的Flask+Vue物业管理系统技术的发
源码地址: https://pan.quark.cn/s/a4b39357ea24 # SerialAssistant串口助手 下载地址: 本仓库release文件夹 在线下载:http://mculover666.cn/SerialAssistant.zip 功能说明 本项目是使用C# + WinForm框架编写的串口助手。 目前版本为2.0.0版本,拥有以下功能: 未打开串口时,自动扫描可用端口 接收数据支持文本或者HEX方式显示 支持接收数据加入时间戳 支持将当前接收数据保存为文件 支持发送文本数据或HEX数据 支持自动定时发送数据 支持从文件中(.txt, .json)加载数据到发送文本框 支持发送数据记录(不重复记录) ……欢迎加入更多功能 环境说明 VS2019 .NET Framework 4.5 教程 C#上位机开发(一)—— 了解上位机 C#上位机开发(二)—— Hello,World C#上位机开发(三)—— 构建SerialAssistant雏形 C#上位机开发(四)—— SerialAssistant功能完善 C#上位机开发(五)——SerialAssistant界面升级(WinForm界面布局进阶) C#上位机开发(六)——SerialAssistant功能优化(串口自动扫描功能、接收数据保存功能、加载发送文件、发送历史记录、打开浏览器功能、定时发送功能) C#上位机开发(七)—— 修改窗口图标和exe文件图标 C#上位机开发(八)—— 美化界面(给按钮添加背景) 更新日志 2018/6/3 完成串口属性设置,打开关闭异常处理; 字符串发送功能; 字符串接收功能; 2018/6/4 完善串口扩展功能界面部分 2018/6/6 完善...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值