MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python数据序列化与反序列化的方式

2023-01-104.6k 阅读

什么是数据序列化与反序列化

在计算机编程领域,数据序列化(Serialization)指的是将数据结构或对象状态转换为可以存储或传输的格式的过程。这一过程使得数据能够在不同环境间进行交换,例如从内存存储到文件系统,或者通过网络发送到其他计算机。而反序列化(Deserialization)则是序列化的逆过程,即将序列化后的数据重新转换回原始的数据结构或对象状态。

在Python中,数据序列化与反序列化有着广泛的应用场景。比如,当我们需要将内存中的数据持久化到磁盘上,以文件的形式保存,就需要进行序列化操作。而当我们再次从文件中读取数据并恢复到程序中使用时,就需要反序列化操作。又比如在网络通信中,数据从一个节点发送到另一个节点,也需要先进行序列化,接收方收到数据后再进行反序列化。

Python中的数据序列化与反序列化方式

JSON序列化与反序列化

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。Python提供了json模块来处理JSON数据的序列化与反序列化。

JSON序列化

在Python中,使用json.dumps()函数将Python对象转换为JSON格式的字符串。例如:

import json

data = {
    "name": "Alice",
    "age": 30,
    "city": "New York"
}

json_str = json.dumps(data)
print(json_str)

在上述代码中,我们定义了一个Python字典data,然后使用json.dumps()函数将其转换为JSON格式的字符串。json.dumps()函数还有一些可选参数,比如indent参数可以使生成的JSON字符串更加美观易读。例如:

import json

data = {
    "name": "Alice",
    "age": 30,
    "city": "New York"
}

json_str = json.dumps(data, indent=4)
print(json_str)

这样生成的JSON字符串会按照缩进4个空格的格式进行排版,更加清晰。

JSON反序列化

使用json.loads()函数将JSON格式的字符串转换回Python对象。例如:

import json

json_str = '{"name": "Alice", "age": 30, "city": "New York"}'
data = json.loads(json_str)
print(data)
print(type(data))

在这段代码中,我们将JSON格式的字符串json_str通过json.loads()函数转换回Python字典对象。需要注意的是,JSON中的数据类型与Python中的数据类型有一定的对应关系。例如,JSON中的对象对应Python中的字典,JSON中的数组对应Python中的列表,JSON中的字符串、数字、布尔值也分别对应Python中的相同类型。

如果要从文件中读取JSON数据并反序列化,可以使用json.load()函数。假设我们有一个名为data.json的文件,内容如下:

{
    "name": "Bob",
    "age": 25,
    "city": "Los Angeles"
}

可以通过以下代码读取并反序列化:

import json

with open('data.json', 'r') as f:
    data = json.load(f)
    print(data)

在上述代码中,我们使用open()函数以只读模式打开data.json文件,然后通过json.load()函数将文件中的JSON数据反序列化为Python对象。

Pickle序列化与反序列化

Pickle是Python特有的一种序列化格式,它可以将几乎所有的Python对象(包括自定义类的实例)进行序列化和反序列化。与JSON相比,Pickle更侧重于Python对象的持久化,而不是数据交换,因为Pickle格式的数据只能在Python环境中使用。

Pickle序列化

使用pickle.dump()函数将Python对象序列化并保存到文件中。例如,我们定义一个简单的类并创建其实例,然后进行序列化:

import pickle


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age


person = Person("Charlie", 28)

with open('person.pkl', 'wb') as f:
    pickle.dump(person, f)

在上述代码中,我们定义了一个Person类,并创建了一个person实例。然后使用pickle.dump()函数将person实例序列化并保存到名为person.pkl的文件中。这里以二进制写入模式'wb'打开文件,因为Pickle序列化后的数据是二进制格式。

Pickle反序列化

使用pickle.load()函数从文件中读取Pickle序列化的数据并反序列化为Python对象。例如:

import pickle


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age


with open('person.pkl', 'rb') as f:
    person = pickle.load(f)
    print(person.name)
    print(person.age)

在这段代码中,我们以二进制读取模式'rb'打开person.pkl文件,然后使用pickle.load()函数将文件中的数据反序列化为Person类的实例person,并可以访问其属性。

需要注意的是,Pickle反序列化存在一定的安全风险。如果从不可信的来源接收并反序列化Pickle数据,可能会导致代码注入攻击。因为Pickle在反序列化过程中会执行一些字节码指令,如果恶意构造这些指令,就可能在反序列化时执行恶意代码。所以在使用Pickle进行反序列化时,一定要确保数据来源的可靠性。

YAML序列化与反序列化

YAML(YAML Ain't Markup Language)是一种人类可读的数据序列化格式,以简洁的语法著称,常用于配置文件等场景。在Python中,可以使用PyYAML库来处理YAML数据的序列化与反序列化。首先需要安装PyYAML库,可以使用pip install PyYAML命令进行安装。

YAML序列化

使用yaml.dump()函数将Python对象转换为YAML格式的字符串。例如:

import yaml

data = {
    "name": "David",
    "age": 35,
    "hobbies": ["reading", "traveling"]
}

yaml_str = yaml.dump(data)
print(yaml_str)

在上述代码中,我们将一个Python字典data通过yaml.dump()函数转换为YAML格式的字符串。YAML的语法格式与Python的字典和列表等数据结构非常相似,所以生成的YAML字符串很容易阅读和理解。

YAML反序列化

使用yaml.load()函数将YAML格式的字符串转换回Python对象。例如:

import yaml

yaml_str = """
name: David
age: 35
hobbies:
  - reading
  - traveling
"""

data = yaml.load(yaml_str, Loader=yaml.FullLoader)
print(data)

在这段代码中,我们定义了一个YAML格式的字符串yaml_str,然后使用yaml.load()函数将其反序列化为Python字典对象。需要注意的是,在较新的PyYAML版本中,为了安全起见,需要显式指定Loader参数,这里使用yaml.FullLoader可以防止一些潜在的安全风险,例如通过YAML进行代码注入。

如果要从文件中读取YAML数据并反序列化,可以使用yaml.load()函数结合文件操作。假设我们有一个名为config.yaml的文件,内容如下:

server:
  host: 127.0.0.1
  port: 8080
database:
  name: mydb
  user: root
  password: pass123

可以通过以下代码读取并反序列化:

import yaml

with open('config.yaml', 'r') as f:
    config = yaml.load(f, Loader=yaml.FullLoader)
    print(config)

在上述代码中,我们以只读模式打开config.yaml文件,然后通过yaml.load()函数将文件中的YAML数据反序列化为Python字典对象,这样就可以方便地读取配置文件中的各项配置信息。

XML序列化与反序列化

XML(eXtensible Markup Language)是一种标记语言,常用于数据存储和传输,特别是在企业级应用和网络服务中。Python提供了多个库来处理XML,其中xml.etree.ElementTree是标准库中用于解析和创建XML文档的模块。

XML序列化

下面是一个将Python数据结构转换为XML格式的示例。假设我们有一个包含书籍信息的列表,要将其序列化为XML:

import xml.etree.ElementTree as ET

books = [
    {
        "title": "Python Crash Course",
        "author": "Eric Matthes",
        "price": 30.0
    },
    {
        "title": "Effective Python",
        "author": "Brett Slatkin",
        "price": 40.0
    }
]

root = ET.Element('books')
for book in books:
    book_elem = ET.SubElement(root, 'book')
    title_elem = ET.SubElement(book_elem, 'title')
    title_elem.text = book['title']
    author_elem = ET.SubElement(book_elem, 'author')
    author_elem.text = book['author']
    price_elem = ET.SubElement(book_elem, 'price')
    price_elem.text = str(book['price'])

tree = ET.ElementTree(root)
tree.write('books.xml', encoding='utf-8', xml_declaration=True)

在上述代码中,我们首先创建了一个根元素books,然后遍历书籍列表,为每本书创建一个book子元素,并在book子元素下创建titleauthorprice子元素,并设置它们的文本值。最后,我们将整个XML树写入到books.xml文件中,并指定编码为utf-8,同时添加XML声明。

XML反序列化

要将XML数据反序列化为Python对象,可以使用xml.etree.ElementTree模块的解析功能。例如,对于上述生成的books.xml文件,我们可以这样反序列化:

import xml.etree.ElementTree as ET

tree = ET.parse('books.xml')
root = tree.getroot()

books = []
for book_elem in root.findall('book'):
    book = {}
    title_elem = book_elem.find('title')
    if title_elem is not None:
        book['title'] = title_elem.text
    author_elem = book_elem.find('author')
    if author_elem is not None:
        book['author'] = author_elem.text
    price_elem = book_elem.find('price')
    if price_elem is not None:
        book['price'] = float(price_elem.text)
    books.append(book)

print(books)

在这段代码中,我们首先使用ET.parse()函数解析books.xml文件得到XML树,然后获取根元素。接着,我们通过findall()方法找到所有的book子元素,并通过find()方法找到每个book子元素下的titleauthorprice子元素,并将其文本值提取出来构建Python字典对象,最后将这些字典对象添加到books列表中。

除了xml.etree.ElementTree模块外,lxml库也是处理XML的常用工具,它提供了更高效的解析和序列化功能,并且支持更多的XML特性,例如XPath查询等。

其他序列化方式

  1. MsgPack序列化与反序列化:MsgPack是一种高效的二进制序列化格式,它类似于JSON,但具有更高的性能和更小的序列化后数据体积。在Python中,可以使用msgpack库进行MsgPack的序列化与反序列化。首先需要安装msgpack库,使用pip install msgpack命令。

MsgPack序列化

import msgpack

data = {
    "message": "Hello, MsgPack!",
    "number": 42
}

packed = msgpack.packb(data)

在上述代码中,使用msgpack.packb()函数将Python字典data序列化为MsgPack格式的二进制数据packed

MsgPack反序列化

import msgpack

packed = msgpack.packb({
    "message": "Hello, MsgPack!",
    "number": 42
})

unpacked = msgpack.unpackb(packed)
print(unpacked)

这里使用msgpack.unpackb()函数将MsgPack格式的二进制数据packed反序列化为Python对象unpacked。MsgPack在处理大量数据或者对性能要求较高的场景下表现出色,例如在网络通信频繁的分布式系统中。

  1. Protocol Buffers序列化与反序列化:Protocol Buffers(简称Protobuf)是Google开发的一种数据序列化协议,它通过定义数据结构的描述文件(.proto文件),然后使用工具生成对应的Python代码来进行序列化和反序列化操作。

首先,定义一个.proto文件,例如person.proto

syntax = "proto3";

message Person {
    string name = 1;
    int32 age = 2;
}

然后,使用protoc工具生成Python代码:

protoc --python_out=. person.proto

这会在当前目录下生成person_pb2.py文件。接下来可以在Python代码中使用生成的代码进行序列化和反序列化:

import person_pb2

person = person_pb2.Person()
person.name = "Eve"
person.age = 22

serialized = person.SerializeToString()

new_person = person_pb2.Person()
new_person.ParseFromString(serialized)
print(new_person.name)
print(new_person.age)

在上述代码中,我们首先创建一个Person对象并设置其属性,然后使用SerializeToString()方法将其序列化为二进制数据。接着,我们创建一个新的Person对象,并使用ParseFromString()方法将二进制数据反序列化为对象。Protobuf在大型项目中,尤其是需要高效数据传输和存储的场景下,有着广泛的应用,例如在微服务架构中用于服务间的数据通信。

不同的序列化与反序列化方式各有优缺点,在实际应用中需要根据具体的需求来选择合适的方式。如在简单的数据交换且需要跨语言使用的场景下,JSON是很好的选择;而对于Python内部对象的持久化,Pickle更为方便;如果需要处理配置文件,YAML简洁的语法可能更合适;在企业级应用和网络服务中,XML有着一定的地位;而对于追求高性能和紧凑数据表示的场景,MsgPack和Protocol Buffers则是不错的选择。