Python对象的序列化和反序列化:第2部分

这是关于序列化和反序列化Python对象的教程的第二部分。 在第一部分中 ,您学习了基础知识,然后深入研究Pickle和JSON的来龙去脉。

在这一部分中,您将探索YAML(确保拥有第1部分中正在运行的示例),讨论性能和安全性考虑,对其他序列化格式进行审查,最后学习如何选择正确的方案。

YAML

YAML是我最喜欢的格式。 这是一种人类友好的数据序列化格式。 与Pickle和JSON不同,它不是Python标准库的一部分,因此您需要安装它:

pip install yaml

yaml模块仅具有load()dump()函数。 默认情况下,它们使用loads()dumps()类的字符串,但是可以使用第二个参数,它是一个开放流,然后可以转储文件或从文件中加载。

import yaml



print yaml.dump(simple)



boolean: true

int_list: [1, 2, 3]

none: null

number: 3.44

text: string

请注意,YAML与Pickle甚至JSON相比如何可读。 现在,关于YAML的最酷的部分是:它了解Python对象! 无需自定义编码器和解码器。 这是使用YAML的复杂序列化/反序列化:

> serialized = yaml.dump(complex)

> print serialized



a: !!python/object:__main__.A

  simple:

    boolean: true

    int_list: [1, 2, 3]

    none: null

    number: 3.44

    text: string

when: 2016-03-07 00:00:00



> deserialized = yaml.load(serialized)

> deserialized == complex

True

如您所见,YAML具有自己的标记Python对象的符号。 输出仍然非常易于阅读。 datetime对象不需要任何特殊标记,因为YAML固有地支持datetime对象。

性能

在开始考虑性能之前,您需要考虑性能是否值得关注。 如果您相对不频繁地对少量数据进行序列化/反序列化(例如,在程序开始时读取配置文件),那么性能并不是真正的问题,您可以继续。

但是,假设您已对系统进行了概要分析并发现序列化和/或反序列化会导致性能问题,那么以下是要解决的问题。

性能有两个方面:序列化/反序列化的速度有多快,序列化的表示量有多大?

为了测试各种序列化格式的性能,我将创建一个较大的数据结构并使用Pickle,YAML和JSON对其进行序列化/反序列化。 big_data列表包含5,000个复杂对象。

big_data = [dict(a=simple, when=datetime.now().replace(microsecond=0)) for i in range(5000)]

泡菜

我将在这里使用IPython来获取方便的%timeit魔术函数,该函数可测量执行时间。

import cPickle as pickle



In [190]: %timeit serialized = pickle.dumps(big_data)

10 loops, best of 3: 51 ms per loop



In [191]: %timeit deserialized = pickle.loads(serialized)

10 loops, best of 3: 24.2 ms per loop



In [192]: deserialized == big_data

Out[192]: True



In [193]: len(serialized)

Out[193]: 747328

默认的pickle序列化需要83.1毫秒,反序列化需要29.2毫秒,序列化的大小为747,328字节。

让我们尝试最高的协议。

In [195]: %timeit serialized = pickle.dumps(big_data, protocol=pickle.HIGHEST_PROTOCOL)

10 loops, best of 3: 21.2 ms per loop



In [196]: %timeit deserialized = pickle.loads(serialized)

10 loops, best of 3: 25.2 ms per loop



In [197]: len(serialized)

Out[197]: 394350

有趣的结果。 序列化时间缩短到只有21.2毫秒,但是反序列化时间却增加了一点到25.2毫秒。 序列化的大小显着缩减至394,350字节(52%)。

JSON格式

In [253] %timeit serialized = json.dumps(big_data, cls=CustomEncoder)

10 loops, best of 3: 34.7 ms per loop



In [253] %timeit deserialized = json.loads(serialized, object_hook=decode_object)

10 loops, best of 3: 148 ms per loop



In [255]: len(serialized)

Out[255]: 730000

好。 在编码方面,性能似乎比Pickle差一些,但在解码方面却差很多很多:慢6倍。 这是怎么回事? 这是object_hook函数的构件,需要为每个字典运行以检查是否需要将其转换为对象。 没有对象钩子的运行要快得多。

%timeit deserialized = json.loads(serialized)

10 loops, best of 3: 36.2 ms per loop

这里的教训是,在序列化和反序列化为JSON时,请务必仔细考虑任何自定义编码,因为它们可能会对整体性能产生重大影响。

YAML

In [293]: %timeit serialized = yaml.dump(big_data)

1 loops, best of 3: 1.22 s per loop



In[294]: %timeit deserialized = yaml.load(serialized)

1 loops, best of 3: 2.03 s per loop



In [295]: len(serialized)

Out[295]: 200091

好。 YAML确实非常缓慢。 但是,请注意一些有趣的事情:序列化的大小仅为200,091字节。 比Pickle和JSON都好得多。 让我们快速地查看内部内容:

In [300]: print serialized[:211]

- a: &id001

    boolean: true

    int_list: [1, 2, 3]

    none: null

    number: 3.44

    text: string

  when: 2016-03-13 00:11:44

- a: *id001

  when: 2016-03-13 00:11:44

- a: *id001

  when: 2016-03-13 00:11:44

YAML在这里非常聪明。 它确定所有5,000个dict都为'a'键共享相同的值,因此它仅存储一次并为所有对象使用*id001进行引用。

安全

安全通常是一个至关重要的问题。 Pickle和YAML依靠构造Python对象而容易受到代码执行攻击。 格式正确的文件可以包含将由Pickle或YAML执行的任意代码。 无需惊慌。 这是设计使然,并在Pickle的文档中进行了记录:

警告:泡菜模块并非旨在防止错误或恶意构建的数据。 切勿挑剔从不可信或未经身份验证的来源收到的数据。

以及YAML的文档中:

警告:使用从不可信来源收到的任何数据调用yaml.load是不安全的! yaml.load与pickle.load一样强大,因此可以调用任何Python函数。

您只需要了解您不应该使用Pickle或YAML加载从不受信任的来源收到的序列化数据。 JSON是可以的,但是如果您有自定义的编码器/解码器,也可以公开。

yaml模块提供了yaml.safe_load()函数,该函数将仅加载简单的对象,但是您将失去很多YAML的功能,并且可能选择仅使用JSON。

其他格式

还有许多其他序列化格式可用。 这里有几个。

原虫

Protobuf或协议缓冲区是Google的数据交换格式。 它用C ++实现,但是具有Python绑定。 它具有复杂的架构,可以高效地打包数据。 非常强大,但使用起来不是很容易。

消息包

MessagePack是另一种流行的序列化格式。 它也是二进制且高效的,但是与Protobuf不同,它不需要架构。 它具有与JSON类似的类型系统,但是功能更丰富。 键可以是任何类型,不仅仅支持字符串和非UTF8字符串。

CBOR

CBOR代表简洁二进制对象表示。 同样,它支持JSON数据模型。 CBOR不像Protobuf或MessagePack众所周知,但由于两个原因而引起人们的兴趣:

  1. 它是一个正式的Internet标准: RFC 7049 。
  2. 它是专门为物联网(IoT)设计的。

如何选择?

这是个大问题。 有这么多种选择,您如何选择? 让我们考虑应该考虑的各种因素:

  1. 序列化格式应该是人类可读和/或人类可编辑的吗?
  2. 是否会从不受信任的来源接收序列化的内容?
  3. 序列化/反序列化是否是性能瓶颈?
  4. 是否需要与非Python环境交换序列化的数据?

我将为您提供方便,并介绍几种常见方案以及我为每种方案推荐的格式:

自动保存Python程序的本地状态

在此处使用HIGHEST_PROTOCOL泡菜(cPickle)。 它快速,高效,并且可以存储和加载大多数Python对象,而无需任何特殊代码。 它也可以用作本地持久性缓存。

配置文件

绝对是YAML。 对于人类需要阅读或编辑的任何内容,没有什么比它的简单性更好。 Ansible和其他许多项目已成功使用它。 在某些情况下,您可能更喜欢使用直接的Python模块作为配置文件。 这可能是正确的选择,但它不是序列化,它实际上是程序的一部分,而不是单独的配置文件。

Web API

JSON很明显是赢家。 如今,Web API最常被本机使用JSONJavaScript Web应用程序使用。 某些Web API可能会返回其他格式(例如,用于密集表格结果集的csv),但是我认为您可以以最小的开销将csv数据打包到JSON中(无需将每行作为具有所有列名的对象重复)。

高容量/低延迟的大规模通信

使用以下二进制协议之一:Protobuf(如果需要模式),MessagePack或CBOR。 运行您自己的测试,以验证每个选项的性能和代表性。

结论

Python对象的序列化和反序列化是分布式系统的重要方面。 您不能直接通过网络发送Python对象。 您通常需要与以其他语言实现的其他系统进行互操作,有时您只想将程序的状态存储在持久性存储中。

Python在其标准库中附带了几种序列化方案,还有更多的可作为第三方模块使用。 了解所有选项以及每个选项的利弊,可以让您选择适合自己情况的最佳方法。

翻译自: https://code.tutsplus.com/tutorials/serialization-and-deserialization-of-python-objects-part-2--cms-26184

你可能感兴趣的:(编程语言,python,java,javascript,linux,ViewUI)