一篇文章了解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实现生成用户信息
Mar 20 Python
Python实现图片转字符画的示例
Aug 22 Python
Python数据分析中Groupby用法之通过字典或Series进行分组的实例
Dec 08 Python
TensorFlow实现创建分类器
Feb 06 Python
Python3.5 Pandas模块缺失值处理和层次索引实例详解
Apr 23 Python
python利用re,bs4,requests模块获取股票数据
Jul 29 Python
WxPython实现无边框界面
Nov 18 Python
django 中使用DateTime常用的时间查询方式
Dec 03 Python
查看端口并杀进程python脚本代码
Dec 17 Python
python IDLE添加行号显示教程
Apr 25 Python
Python爬虫爬取微博热搜保存为 Markdown 文件的源码
Feb 22 Python
python通配符之glob模块的使用详解
Apr 24 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
DSP接收机前端设想
2021/03/02 无线电
php使用CURL伪造IP和来源实例详解
2015/01/15 PHP
php时间计算相关问题小结
2016/05/09 PHP
PHP实现生成带背景的图形验证码功能
2016/10/03 PHP
详解php中serialize()和unserialize()函数
2017/07/08 PHP
PHP简单实现记录网站访问量功能示例
2018/06/06 PHP
详解PHP的抽象类和抽象方法以及接口总结
2019/03/15 PHP
优秀js开源框架-jQuery使用手册(1)
2007/03/10 Javascript
Jquery 数据选择插件Pickerbox使用介绍
2012/08/24 Javascript
js获取对象为null的解决方法
2013/11/21 Javascript
SeaJS入门教程系列之完整示例(三)
2014/03/03 Javascript
js图片轮播手动切换效果
2015/11/10 Javascript
jQuery实现ajax调用WCF服务的方法(附带demo下载)
2015/12/04 Javascript
用jquery快速解决IE输入框不能输入的问题
2016/10/04 Javascript
微信小程序页面间通信的5种方式
2017/03/31 Javascript
JavaScript实现QQ聊天消息展示和评论提交功能
2017/05/22 Javascript
快速搭建React的环境步骤详解
2017/11/06 Javascript
vue项目中引入noVNC远程桌面的方法
2018/03/05 Javascript
angular 实现的输入框数字千分位及保留几位小数点功能示例
2018/06/19 Javascript
Node.js net模块功能及事件监听用法分析
2019/01/05 Javascript
python单元测试unittest实例详解
2015/05/11 Python
python运行时强制刷新缓冲区的方法
2019/01/14 Python
Python实现根据日期获取当天凌晨时间戳的方法示例
2019/04/09 Python
Python异常处理例题整理
2019/07/07 Python
python和js交互调用的方法
2020/06/23 Python
Pytest测试框架基本使用方法详解
2020/11/25 Python
Pandas的数据过滤实现
2021/01/15 Python
GUESS Factory加拿大:牛仔裤、服装及配饰
2019/09/20 全球购物
管理心得体会
2013/12/28 职场文书
国际贸易专业个人求职信格式
2014/02/02 职场文书
我的祖国演讲稿
2014/05/04 职场文书
搞笑车尾标语
2014/06/23 职场文书
财务管理专业自荐书
2014/09/02 职场文书
计算机专业自荐信范文
2015/03/26 职场文书
2016年4月份红领巾广播稿
2015/12/21 职场文书
通知怎么写?
2019/04/17 职场文书