关于我
编程界的一名小小程序猿,目前在一个创业团队任team lead,技术栈涉及Android、Python、Java和Go,这个也是我们团队的主要技术栈。 联系:hylinux1024@gmail.com

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文件读写的支持。这也是marshalPython版本不兼容的原因。开发者如果要使用序列化/反序列化,那么应该使用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_PROTOCOLpickle.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它与marshalpickle一样拥有相似的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格式文档中反序列化成一个对象

jsonPython对象的转化对照表

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 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-8
import jsonif __name__ == '__main__':with open("complex_data.json") as complex_data:# 这里不指定object_hookz2 = 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 dumpsreturn _default_encoder.encode(obj)File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 199, in encodechunks = self.iterencode(o, _one_shot=True)File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 257, in iterencodereturn _iterencode(o, 0)File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/encoder.py", line 179, in defaultraise 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] = datereturn 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常见的序列化工具有marshalpicklejsonmarshal主要用于Python.pyc文件,并与Python版本相关。它不能序列化用户定义的类。 picklePython对象的序列化工具则比marshal更通用些,它可以兼容Python的不同版本。json是一种语言无关的数据结构,广泛用于各种网络应用尤其在REST API的服务中的数据交互。

0x04 学习资料

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

转载于:https://juejin.im/post/5d0362755188255e780b6815

一文了解Python常见的序列化操作相关推荐

  1. python常见的文件操作:打开、创建等

    import osdata_path = 'xxx/xxx/xxx.txt' data1_path = 'yyy/yyy/yyy.txt' path = 'zzz/zzz/zzz'if not os. ...

  2. Python常见数据框操作①

    import numpy as np import pandas as pd from pandas import Sereis, DataFrame ser = Series(np.arange(3 ...

  3. 新技能 | 使用python代码来高效操作Excel表格 (文末赠书5本)

    新技能 | 使用python代码来高效操作Excel表格 (文末赠书5本) 在日常学习和工作中,少不了要跟Excel表格打交道.而我们作为程序猿,深知代码编程给人带来的便捷性,那我们怎么样使用代码来操 ...

  4. python json dumps 自定义_Python json.dumps 自定义序列化操作

    def login_ajax(request): if request.method == "GET": return render(request, 'login_ajax.ht ...

  5. ios php 序列化,PHP常见的序列化与反序列化操作实例分析

    本文实例讲述了PHP常见的序列化与反序列化操作.分享给大家供大家参考,具体如下: 1.概念 serialize() 把变量和它们的值编码成文本形式 unserialize() 恢复原先变量 2.序列化 ...

  6. python列表按照指定顺序排序-Python常见排序操作示例【字典、列表、指定元素等】...

    本文实例讲述了Python常见排序操作.分享给大家供大家参考,具体如下: 字典排序 按value排序 d1 = {"name":"python","b ...

  7. python软件加密、固定机器使用_如何用Python进行最常见的加密操作?(附最新400集Python教程)...

    前言 我们所说的加密方式,都是对二进制编码的格式进行加密的,对应到Python中,则是我们的Bytes. 所以当我们在Python中进行加密操作的时候,要确保我们操作的是Bytes,否则就会报错. 将 ...

  8. 使用python对word文档进行另存为、合并操作处理

    对word进行另存为 使用python对word进行操作时,需要先引用win32com的包 import win32com.client as win32 word = win32.gencache. ...

  9. 一文详解python中的数据库操作

    python中的数据库操作 一.数据库编程接口 1. 连接对象 二.使用内置的SQLite 1.创建数据库文件 2.操作SQLite 三.MySql数据库的使用 3.1 安装MySql 3.2 设置环 ...

最新文章

  1. 汉诺塔怎么加计数次数c语言,C语言计算汉诺塔最小移动步数 (二)
  2. What you should know about .so files
  3. TCP连接——三次握手和四次断开
  4. 自定义C++异常处理
  5. redis指定配置文件启动_redis基础知识整理-安装
  6. 明知道计算机是吃青春饭,为什么还有那么多人前仆后继
  7. MTK:socket通信
  8. python爬虫工程师工作内容_爬虫岗位职责
  9. 推荐10篇jQuery技术的文章
  10. Servlet JSP 面试题
  11. IPV4组播地址解析以及IPV4地址详解
  12. Nginx 重定向 80 到443
  13. vue PC端菜单优化(第一个菜单隐藏的时候,整个菜单都不显示)
  14. 天梯赛题目练习——打印杨辉三角(附带PTA测试点)
  15. MyBatis--对象的联合查询
  16. 高速电路PCB布局布线参考
  17. 在docker下进行ETH并行训练和在本机下进行ETH并行训练
  18. qt中ui的 使用介绍
  19. 游戏装备强化java机制,游戏装备强化类问题的数学期望
  20. 【数据挖掘】用户画像

热门文章

  1. Shiro源码学习之一
  2. CUDA Samples: image normalize(mean/standard deviation)
  3. C++11中Lambda表达式的使用
  4. 【Dlib】dlib和opencv的互转
  5. 【opencv】ubuntu14.04上编译opencv-4.0.1 + opencv_contrib-4.0.1
  6. Linux中bashrc河bash_profile
  7. android倒计时实现方法,Android实现倒计时方法汇总
  8. access“idno”字段改为文本型_结构化文本计算示例(一)
  9. 如何做到微信机器人不封号_电销系统是如何做到不封号的?封号对企业有什么影响?...
  10. python name_python中__name__的使用