一篇文章了解Python中常见的序列化操作


Posted in Python onJune 20, 2019

0x00 marshal

marshal使用的是与Python语言相关但与机器无关的二进制来读写Python对象的。这种二进制的格式也跟Python语言的版本相关,marshal序列化的格式对不同的版本的Python是不兼容的。

marshal一般用于Python内部对象的序列化。

一般地包括:

  • 基本类型 booleans, integers,floating point numbers,complex numbers
  • 序列集合类型 strings, bytes, bytearray, tuple, list, set, frozenset, dictionary
  • code对象 code object
  • 其它类型 None, Ellipsis, StopIteration

marshal的主要作用是对Python“编译”的.pyc文件读写的支持。这也是marshal对Python版本不兼容的原因。开发者如果要使用序列化/反序列化,那么应该使用pickle模块。

常见的方法

marshal.dump(value, file[, version])

序列化一个对象到文件中

marshal.dumps(value[, version])

序列化一个对象并返回一个bytes对象

marshal.load(file)

从文件中反序列化一个对象

marshal.loads(bytes)

从bytes二进制数据中反序列化一个对象

0x01 pickle

pickle模块也能够以二进制的方式对Python对象进行读写。相比marshal提供基本的序列化能力,pickle的序列化应用更加广泛。

pickle序列化后的数据也是与Python语言相关的,即其它语言例如Java无法读取由Python通过pickle序列化的二进制数据。如果要使用与语言无法的序列化那么我们应该使用json。下文将会说明。

能被pickle序列化的数据类型有:

  • None, True, and False
  • integers, floating point numbers, complex numbers
  • strings, bytes, bytearrays
  • tuples, lists, sets, and dictionaries 以及包含可以被pickle序列化对象
  • 在模块顶层定义的函数对象 (使用 def定义的, 而不是 lambda表达式)
  • 在模块顶层定义内置函数
  • 在模式顶层定义的类
  • 一个类的__dict__包含了可序列化的对象或__getstate__()方法返回了能够被序列化的对象

如果pickle一个不支持序列化的对象时将会抛出PicklingError。

常见的方法

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

将obj对象序列化到一个file文件中,该方法与Pickler(file, protocol).dump(obj)等价。

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

将obj对象序列化成bytes二进制数据。

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

从file文件中反序列化一个对象,该方法与Unpickler(file).load()等价。

pickle.loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict")

从二进制数据bytes_object反序列化对象。

序列化例子

import pickle

# 定义了一个包含了可以被序列化对象的字典
data = {
 'a': [1, 2.0, 3, 4 + 6j],
 'b': ("character string", b"byte string"),
 'c': {None, True, False}
}

with open('data.pickle', 'wb') as f:
 # 序列化对象到一个data.pickle文件中
 # 指定了序列化格式的版本pickle.HIGHEST_PROTOCOL
 pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)

执行之后在文件夹中多一个data.pickle文件

serialization
├── data.pickle
├── pickles.py
└── unpickles.py

反序列化例子

import pickle

with open('data.pickle', 'rb') as f:
 # 从data.pickle文件中反序列化对象
 # pickle能够自动检测序列化文件的版本
 # 所以这里可以不用版本号
 data = pickle.load(f)

 print(data)

# 执行后结果
# {'a': [1, 2.0, 3, (4+6j)], 'b': ('character string', b'byte string'), 'c': {False, True, None}}

0x02 json
json是与语言无关,非常通用的数据交互格式。在Python它与marshal和pickle一样拥有相似的API。

常见的方法

json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

序列化对象到fp文件中

json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)

将obj序列化成json对象

json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

从文件中反序列化成一个对象

json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

从json格式文档中反序列化成一个对象

json与Python对象的转化对照表

JSON Python
object dict
list,tuple array
str string
int, float, int- & float-derived Enums number
True true
False false
None null

对于基本类型、序列、以及包含基本类型的集合类型json都可以很好的完成序列化工作。

序列化例子

>>> import json
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> print(json.dumps("\"foo\bar"))
"\"foo\bar"
>>> print(json.dumps('\u1234'))
"\u1234"
>>> print(json.dumps('\\'))
"\\"
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
{"a": 0, "b": 0, "c": 0}
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump(['streaming API'], io)
>>> io.getvalue()
'["streaming API"]'

反序列化例子

>>> import json
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]
>>> json.loads('"\\"foo\\bar"')
'"foo\x08ar'
>>> from io import StringIO
>>> io = StringIO('["streaming API"]')
>>> json.load(io)
['streaming API']

对于object的情况就复杂一些了

例如定义了复数complex对象的json文档

complex_data.json

{
 "__complex__": true,
 "real": 42,
 "imaginary": 36
}

要把这个json文档反序列化成Python对象,就需要定义转化的方法

# coding=utf-8
import json

# 定义转化函数,将json中的内容转化成complex对象
def decode_complex(dct):
 if "__complex__" in dct:
  return complex(dct["real"], dct["imaginary"])
 else:
  return dct

if __name__ == '__main__':
 with open("complex_data.json") as complex_data:
  # object_hook指定转化的函数
  z = json.load(complex_data, object_hook=decode_complex)
  print(type(z))
  print(z)

# 执行结果
# <class 'complex'>
# (42+36j)

如果不指定object_hook,那么默认将json文档中的object转成dict

# coding=utf-8
import json

if __name__ == '__main__':

 with open("complex_data.json") as complex_data:
  # 这里不指定object_hook
  z2 = json.loads(complex_data.read())
  print(type(z2))
  print(z2)
# 执行结果
# <class 'dict'>
# {'__complex__': True, 'real': 42, 'imaginary': 36}

可以看到json文档中的object转成了dict对象。

一般情况下这样使用似乎也没什么问题,但如果对类型要求很高的场景就需要明确定义转化的方法了。

除了object_hook参数还可以使用json.JSONEncoder

import json

class ComplexEncoder(json.JSONEncoder):
 def default(self, obj):
  if isinstance(obj, complex):
   # 如果complex对象这里转成数组的形式
   return [obj.real, obj.imag]
   # 默认处理
  return json.JSONEncoder.default(self, obj)

if __name__ == '__main__':
 c = json.dumps(2 + 1j, cls=ComplexEncoder)
 print(type(c))
 print(c)

# 执行结果
# <class 'str'>
# [2.0, 1.0]

因为json模块并不是对所有类型都能够自动完成序列化的,对于不支持的类型,会直接抛出TypeError。

>>> import datetime
>>> d = datetime.datetime.now()
>>> dct = {'birthday':d,'uid':124,'name':'jack'}
>>> dct
{'birthday': datetime.datetime(2019, 6, 14, 11, 16, 17, 434361), 'uid': 124, 'name': 'jack'}
>>> json.dumps(dct)
Traceback (most recent call last):
 File "<pyshell#19>", line 1, in <module>
 json.dumps(dct)
 File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 231, in dumps
 return _default_encoder.encode(obj)
 File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 199, in encode
 chunks = self.iterencode(o, _one_shot=True)
 File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 257, in iterencode
 return _iterencode(o, 0)
 File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 179, in default
 raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable

对于不支持序列化的类型例如datetime以及自定义类型,就需要使用JSONEncoder来定义转化的逻辑。

import json
import datetime

# 定义日期类型的JSONEncoder
class DatetimeEncoder(json.JSONEncoder):

 def default(self, obj):
  if isinstance(obj, datetime.datetime):
   return obj.strftime('%Y-%m-%d %H:%M:%S')
  elif isinstance(obj, datetime.date):
   return obj.strftime('%Y-%m-%d')
  else:
   return json.JSONEncoder.default(self, obj)

if __name__ == '__main__':
 d = datetime.date.today()
 dct = {"birthday": d, "name": "jack"}
 data = json.dumps(dct, cls=DatetimeEncoder)
 print(data)

# 执行结果
# {"birthday": "2019-06-14", "name": "jack"}

现在我们希望发序列化时,能够将json文档中的日期格式转化成datetime.date对象,这时就需要使用到json.JSONDecoder了。

# coding=utf-8
import json
import datetime

# 定义Decoder解析json
class DatetimeDecoder(json.JSONDecoder):

 # 构造方法
 def __init__(self):
  super().__init__(object_hook=self.dict2obj)

 def dict2obj(self, d):
  if isinstance(d, dict):
   for k in d:
    if isinstance(d[k], str):
     # 对日期格式进行解析,生成一个date对象
     dat = d[k].split("-")
     if len(dat) == 3:
      date = datetime.date(int(dat[0]), int(dat[1]), int(dat[2]))
      d[k] = date
  return d

if __name__ == '__main__':
 d = datetime.date.today()
 dct = {"birthday": d, "name": "jack"}
 data = json.dumps(dct, cls=DatetimeEncoder)
 # print(data)

 obj = json.loads(data, cls=DatetimeDecoder)
 print(type(obj))
 print(obj)

# 执行结果
# {"birthday": "2019-06-14", "name": "jack"}
# <class 'dict'>
# {'birthday': datetime.date(2019, 6, 14), 'name': 'jack'}

0x03 总结一下

Python常见的序列化工具有marshal、pickle和json。marshal主要用于Python的.pyc文件,并与Python版本相关。它不能序列化用户定义的类。

pickle是Python对象的序列化工具则比marshal更通用些,它可以兼容Python的不同版本。json是一种语言无关的数据结构,广泛用于各种网络应用尤其在REST API的服务中的数据交互。

0x04 学习资料

  • docs.python.org/3/library/m…
  • docs.python.org/3/library/p…
  • docs.python.org/3/library/j…

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
PHP魔术方法__ISSET、__UNSET使用实例
Nov 25 Python
连接Python程序与MySQL的教程
Apr 29 Python
python实现二维码扫码自动登录淘宝
Dec 27 Python
Python正则抓取新闻标题和链接的方法示例
Apr 24 Python
详解python3中socket套接字的编码问题解决
Jul 01 Python
Python编程之gui程序实现简单文件浏览器代码
Dec 08 Python
Python模块的加载讲解
Jan 15 Python
python绘制漏斗图步骤详解
Mar 04 Python
python多线程分块读取文件
Aug 29 Python
Keras使用ImageNet上预训练的模型方式
May 23 Python
Python实现扫码工具的示例代码
Oct 09 Python
Python暴力破解Mysql数据的示例
Nov 09 Python
python集合是否可变总结
Jun 20 #Python
Django如何自定义model创建数据库索引的顺序
Jun 20 #Python
pyqt 多窗口之间的相互调用方法
Jun 19 #Python
pyqt5 实现多窗口跳转的方法
Jun 19 #Python
快速解决pyqt5窗体关闭后子线程不同时退出的问题
Jun 19 #Python
Pyqt5 实现跳转界面并关闭当前界面的方法
Jun 19 #Python
pyqt5使用按钮进行界面的跳转方法
Jun 19 #Python
You might like
初探PHP5
2006/10/09 PHP
php 字符过滤类,用于过滤各类用户输入的数据
2009/05/27 PHP
php支付宝在线支付接口开发教程
2016/09/19 PHP
yii 2.0中表单小部件的使用方法示例
2017/05/23 PHP
PHP pthreads v3下的Volatile简介与使用方法示例
2020/02/21 PHP
通过PHP实现用户注册后邮箱验证激活
2020/11/10 PHP
ExtJs 3.1 XmlTreeLoader Example Error
2010/02/09 Javascript
Javascript 面向对象 命名空间
2010/05/13 Javascript
基于jquery实现的一个选择中国大学的弹框 (数据、步骤、代码)
2012/07/26 Javascript
jQuery实现定时读取分析xml文件的方法
2015/07/16 Javascript
基于JS判断iframe是否加载成功的方法(多种浏览器)
2016/05/13 Javascript
浅谈jQuery中ajaxPrefilter的应用
2016/08/01 Javascript
Bootstrap实现带动画过渡的弹出框
2016/08/09 Javascript
JS实现兼容火狐及IE iframe onload属性的遮罩层隐藏及显示效果
2016/08/23 Javascript
AngularJS  自定义指令详解及实例代码
2016/09/14 Javascript
深入浅析Node.js单线程模型
2017/07/10 Javascript
vue-cli脚手架的安装教程图解
2018/09/02 Javascript
通过实例解析js简易模块加载器
2019/06/17 Javascript
微信小程序中target和currentTarget的区别小结
2020/11/06 Javascript
[04:04]显微镜下的DOTA2第六期——电影级别的华丽团战
2014/06/20 DOTA
python小技巧之批量抓取美女图片
2014/06/06 Python
python中MySQLdb模块用法实例
2014/11/10 Python
用python 制作图片转pdf工具
2015/01/30 Python
Python基于回溯法子集树模板解决找零问题示例
2017/09/11 Python
pandas全表查询定位某个值所在行列的方法
2018/04/12 Python
python批量识别图片指定区域文字内容
2019/04/30 Python
Python使用正则实现计算字符串算式
2019/12/29 Python
Python类绑定方法及非绑定方法实例解析
2020/10/09 Python
HTML5实现获取地理位置信息并定位功能
2015/04/25 HTML / CSS
英国独特家具和家庭用品购物网站:Cuckooland
2020/08/30 全球购物
运动会通讯稿300字
2014/02/02 职场文书
仓管员岗位责任制
2014/02/19 职场文书
绿色环保口号
2014/06/12 职场文书
艺术节开幕词
2015/01/28 职场文书
党员反四风学习心得体会
2016/01/22 职场文书
世界上超棒的8种逻辑思维
2019/08/06 职场文书