一篇文章了解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 相关文章推荐
Python递归函数定义与用法示例
Jun 02 Python
python调用系统ffmpeg实现视频截图、http发送
Mar 06 Python
python接口自动化(十七)--Json 数据处理---一次爬坑记(详解)
Apr 18 Python
Django Rest framework频率原理与限制
Jul 26 Python
Tensorflow限制CPU个数实例
Feb 06 Python
浅谈spring boot 集成 log4j 解决与logback冲突的问题
Feb 20 Python
Python tkinter 下拉日历控件代码
Mar 04 Python
tensorflow 大于某个值为1,小于为0的实例
Jun 30 Python
爬虫代理的cookie如何生成运行
Sep 22 Python
Pytest allure 命令行参数的使用
Apr 18 Python
pytorch实现手写数字图片识别
May 20 Python
pytorch Dropout过拟合的操作
May 27 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
开发跨浏览器javascript常见注意事项
2009/01/01 Javascript
AlertBox 弹出层信息提示框效果实现步骤
2010/10/11 Javascript
javascript标签在页面中的位置探讨
2013/04/11 Javascript
Ajax同步与异步传输的示例代码
2013/11/21 Javascript
JavaScript中的原型链prototype介绍
2014/12/30 Javascript
JavaScript中的逻辑判断符&amp;&amp;、||与!介绍
2014/12/31 Javascript
javascript发送短信验证码实现代码
2015/11/12 Javascript
AngularJS基础 ng-repeat 指令简单示例
2016/08/03 Javascript
实例浅析js的this
2016/12/11 Javascript
js+html5实现复制文字按钮
2017/07/15 Javascript
jquery根据name取得select选中的值实例(超简单)
2018/01/25 jQuery
浅析js中mvvm模式实现的原理
2018/10/06 Javascript
JS实现返回上一页并刷新页面的方法分析
2019/07/16 Javascript
浅谈JS中this在各个场景下的指向
2019/08/14 Javascript
js模拟F11页面全屏显示
2019/09/17 Javascript
使用layer模态框给新页面传值的方法
2019/09/27 Javascript
js+canvas实现简单扫雷小游戏
2021/01/22 Javascript
jquery实现抽奖功能
2020/10/22 jQuery
[01:14:35]DOTA2上海特级锦标赛B组资格赛#1 Alliance VS Fnatic第一局
2016/02/26 DOTA
[01:01:01]完美世界DOTA2联赛循环赛 GXR vs FTD BO2第一场 10.29
2020/10/29 DOTA
Python sorted函数详解(高级篇)
2018/09/18 Python
详解在Python中以绝对路径或者相对路径导入文件的方法
2019/08/30 Python
Python如何访问字符串中的值
2020/02/09 Python
Python读取表格类型文件代码实例
2020/02/17 Python
在django admin详情表单显示中添加自定义控件的实现
2020/03/11 Python
Python小白学习爬虫常用请求报头
2020/06/03 Python
使用Python FastAPI构建Web服务的实现
2020/06/08 Python
pandas 数据类型转换的实现
2020/12/29 Python
Rakuten Kobo台湾:电子书、eReaders和Reading应用程式
2017/11/24 全球购物
华为慧通笔试题
2016/04/22 面试题
什么是.net的Remoting技术
2016/07/08 面试题
linux面试题参考答案(6)
2014/08/29 面试题
求职自荐书范文
2013/12/04 职场文书
现实表现材料范文
2014/12/23 职场文书
CSS3点击按钮圆形进度打钩效果的实现代码
2021/03/30 HTML / CSS
tensorflow中的数据类型dtype用法说明
2021/05/26 Python