Python序列化---Pickle模块【大量数据的情况下,如何提高Python读写数据的速度?】...

点击上方“潜心的Python小屋”关注我们,第一时间推送优质文章。

前言

大家好,我是潜心。最近在复现2018年阿里提出的CTR预估模型---DIN,关于原生数据的处理以及数据集的构建,因为数据量过于庞大,开源代码使用了Pickle模块。后来查阅相关资料了解后,发现原来是Python标准库中用于数据序列化的模块。在此进行总结记录。

本文约2.5k字,预计阅读10分钟。

Python序列化---Pickle模块【大量数据的情况下,如何提高Python读写数据的速度?】..._第1张图片

Pickle模块---对象序列化

序列化

序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。Python编程核心的序列化机制是pickle标准库。

Pickle---Python序列化

模块 pickle 实现了对一个 Python 对象结构的二进制序列化和反序列化。 "pickling" 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程,而 "unpickling" 是相反的操作,会将(来自一个 binary file 或者 bytes-like object 的)字节流转化回一个对象层次结构。pickling(和 unpickling)也被称为序列化(和反序列化)。

注:pickle 模块并不安全。只应该对信任的数据进行unpickle操作。构建恶意的 pickle 数据来在解封时执行任意代码是可能的。绝对不要对不信任来源的数据和可能被篡改过的数据进行解封。在处理不信任数据时,更安全的序列化格式如 json更为适合。

Pickle序列化的功能与好处

pickle的功能就是把你上次计算得到的数据保存起来,当你需要使用这些数据时,直接通过load将数据进行恢复,这样的好处有:

  1. 不需要重新去计算得到数据,节省计算机资源;

  2. 可以更好的被内存调用,不需要经过数据格式的转换,提高效率;【直接保存为其他格式,如txt、csv的数据读写速度都不如序列化后的数据(字节流)】

  3. pickle可以保存多个对象。实验中,同一数据集下需要保存的内容不止一种,通过pickle可以全部进行保存到一个.pkl文件。

Pickle与JSON的比较

Pickle 协议和 JSON (JavaScript Object Notation) 间有着本质的不同:

  • JSON 是一个文本序列化格式(它输出 unicode 文本),而 pickle 是一个二进制序列化格式;

  • JSON 是可以阅读的(.json文件可以直接打开),而 pickle 不能(.pickle文件);

  • JSON是可互操作的,在Python系统之外广泛使用,而pickle则是Python专用的;

  • 默认情况下,JSON 只能表示 Python 内置类型的子集,不能表示自定义的类;但 pickle 可以表示大量的 Python 数据类型;

  • 不像pickle,对一个不信任的JSON进行反序列化的操作本身不会造成任意代码执行漏洞;

数据流格式

pickle 所使用的数据格式仅可用于 Python。这样做的好处是没有外部标准给该格式强加限制,比如 JSON 或 XDR(不能表示共享指针)标准;但这也意味着非 Python 程序可能无法重新读取 pickle 封存的 Python 对象。

默认情况下,pickle 格式使用相对紧凑的二进制来存储。

当前共有 6 种不同的协议可用于序列化操作。使用的协议版本越高,读取所生成 pickle 对象所需的 Python 版本就要越新。【v1~v5】

模块接口

要piclking某个包含层次结构的对象,只需调用 dumps() 函数即可。同样,要unpickling数据流,可以调用 loads() 函数。但是,如果要对序列化和反序列化加以更多的控制,可以分别创建 Pickler 或 Unpickler 对象。

模块常量

  • pickle.HIGHEST_PROTOCOL

    整数,可用的最高协议版本。此值可以作为协议值传递给 dump() 和 dumps() 函数,以及 Pickler 的构造函数。

  • pickle.DEFAULT_PROTOCOL

    整数,用于 pickle 数据的默认协议版本。它可能小于 HIGHEST_PROTOCOL。当前默认协议是 v4。

模块方法

  • pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)

    将对象 obj 序列化以后的对象写入已打开的file。它等同于 Pickler(file, protocol).dump(obj)

  • pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)

    将 obj 序列化以后的对象作为 bytes 类型直接返回,而不是将其写入到文件。

  • pickle.load(file, ***, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)

    从已打开的file文件中读取封存后的对象,重建其中特定对象的层次结构并返回。它相当于 Unpickler(file).load()。Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。

  • pickle.loads(data, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)

    重建并返回一个对象的序列化表示形式 data 的对象层级结构。

模块异常

  • exception pickle.PickleError

    其他 pickle 异常的基类。它是 Exception 的一个子类。

  • exception pickle.PicklingError

    当 Pickler 遇到无法解封的对象时抛出此错误。它是 PickleError 的子类。

  • exception pickle.UnpicklingError

    当解封出错时抛出此异常,例如数据损坏或对象不安全。它是 PickleError 的子类。

模块类【只介绍主要方法】

  • class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)

    它接受一个二进制文件用于写入 pickle 数据流。

    • dump(obj):将 obj 封存后的内容写入已打开的文件对象,该文件对象已经在构造函数中指定。

  • class pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)

    它接受一个二进制文件用于读取 pickle 数据流。

    • load():从构造函数中指定的文件对象里读取封存好的对象,重建其中特定对象的层次结构并返回。

  • class pickle.PickleBuffer(buffer)

    缓冲区的包装器 (wrapper),缓冲区中包含着可封存的数据。

可以被Pickling/Unpickling的对象

下列类型可以实现:

  • NoneTrue 和 False

  • 整数、浮点数、复数;

  • str、byte、bytearray;

  • 只包含可封存对象的集合,包括 tuple、list、set 和 dict;

  • 定义在模块最外层的函数(使用 def 定义,lambda 函数则不可以);

  • 定义在模块最外层的内置函数;

  • 定义在模块最外层的类;

实例

一般情况下,我们只使用dump()函数和load()函数进行序列化。

例1

将单个对象保存到.pkl文件:

import pickle

# An arbitrary collection of objects supported by pickle.
data = {
    'a': [1, 2.0, 3, 4+6j],
    'b': ("character string", b"byte string"),
    'c': {None, True, False}
}

with open('data.pkl', 'wb') as f:
    # Pickle the 'data' dictionary using the highest protocol available.
    pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

读取之前的对象:

import pickle

with open('data.pickle', 'rb') as f:
    # The protocol version used is detected automatically, so we do not
    # have to specify it.
    data = pickle.load(f)

例2

也可以将多个对象保存到.pkl文件中,在实验或比赛中,常常要对整个数据集进行多步处理,保存一些必要的数据,单使用pandas的DataFrame结构无法方便的进行保存,因此使用pickle数据格式对所有对象进行序列化保存。

# 写入dataset.pkl文件
with open('dataset/dataset.pkl', 'wb') as f:
    # dataframe数据结构
    pickle.dump(train_set, f, pickle.HIGHEST_PROTOCOL)
    # dataframe数据结构
    pickle.dump(test_set, f, pickle.HIGHEST_PROTOCOL)
    # list数据结构
    pickle.dump(cate_list, f, pickle.HIGHEST_PROTOCOL)
    # tuple数据结构
    pickle.dump((user_count, item_count, cate_count, max_sl), f, pickle.HIGHEST_PROTOCOL)

读取:

with open('dataset/dataset.pkl', 'rb') as f:
  train_set = np.array(pickle.load(f))
  test_set = pickle.load(f)
  cate_list = pickle.load(f)
  user_count, item_count, cate_count, max_sl = pickle.load(f)

总结

Python编程核心的序列化机制是pickle标准库。在数据量大的实验中,合理的利用可以提高数据读写的效率。

往期精彩回顾

第一次参赛---2020腾讯广告算法大赛Baseline思考与分析

Pandas笔记---通过比赛整理出的10条Pandas实用技巧

Pandas笔记---深入Groupby,它的功能没有你想的这么简单

机器学习笔记---信息熵

爬虫基础知识(一)多线程与threading模块

爬虫实战(三)----使用百度API获取经纬度/地址

扫码关注更多精彩

我就知道你“在看”

你可能感兴趣的:(Python序列化---Pickle模块【大量数据的情况下,如何提高Python读写数据的速度?】...)