侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

Python  /  管理员 发布于 7年前   209

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 picklewith 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-8import json# 定义转化函数,将json中的内容转化成complex对象def decode_complex(dct): if "__complex__" in dct:  return complex(dct["real"], dct["imaginary"]) else:  return dctif __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-8import jsonif __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 jsonclass 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 jsonimport datetime# 定义日期类型的JSONEncoderclass 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-8import jsonimport datetime# 定义Decoder解析jsonclass 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 dif __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集合是否可变总结
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客