Synopsis: Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。json模块的dumps()和loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性
Python程序运行时,对象都存在于内存中,一旦程序结束,这些对象所占用的内存就被操作系统全部回收。可以使用pickle
模块将对象保存到磁盘文件中,这种将对象从内存中变成可存储或可传输的过程叫做序列化
,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化
(unpickling)
pickle.dumps()
方法把任意对象序列化成一个bytes
,然后,就可以把这个bytes
写入一个file-like object
,比如使用open()
方法中的wb
模式打开的文件描述符、BytesIO,参考:http://www.madmalls.com/blog/post/read-write-files-in-python/#62-bytesio
In [1]: import pickle In [2]: d = {'name': 'wangy', 'age': 18} In [3]: pickle.dumps(d) Out[3]: b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00wangyq\x02X\x03\x00\x00\x00ageq\x03K\x12u.' In [4]: b = pickle.dumps(d) In [5]: b Out[5]: b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00wangyq\x02X\x03\x00\x00\x00ageq\x03K\x12u.' In [6]: f = open('/tmp/test.txt', 'xb') In [7]: f.write(b) Out[7]: 43 In [8]: f.close()
此时,创建了/tmp/test.txt
文件,里面的数据就是刚写入的字节流,但是无法正常查看内容:
[root@CentOS ~]# file /tmp/test.txt /tmp/test.txt: 8086 relocatable (Microsoft) [root@CentOS ~]# cat /tmp/test.txt }q(XnameqXwangyqXageqKu.[root@CentOS ~]#
pickle.dump()
方法直接
将对象序列化后写入一个file-like object
,比如使用open()
方法中的wb
模式打开的文件描述符、BytesIO,参考:http://www.madmalls.com/blog/post/read-write-files-in-python/#62-bytesio
In [1]: import pickle In [2]: d = {'name': 'wangy', 'age': 18} In [3]: f = open('/tmp/test2.txt', 'xb') In [4]: pickle.dump(d, f) In [5]: f.close()
可以先以rb
模式打开上一步骤中的文件,返回bytes
,然后用pickle.loads()
反序列化出之前的字典对象(只是内容相同,是不同的对象,id不同):
In [1]: import pickle In [2]: f = open('/tmp/test.txt', 'rb') In [3]: b = f.read() In [4]: b Out[4]: b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00wangyq\x02X\x03\x00\x00\x00ageq\x03K\x12u.' In [5]: d = pickle.loads(b) In [6]: d Out[6]: {'name': 'wangy', 'age': 18} In [7]: f.close()
pickle.load()
方法直接从一个file-like object
中反序列化出对象
In [1]: import pickle In [2]: f = open('/tmp/test.txt', 'rb') In [3]: d = pickle.load(f) In [4]: d Out[4]: {'name': 'wangy', 'age': 18} In [5]: f.close()
Python内置的json
模块提供了非常完善的Python对象到JSON格式的转换
json.dumps()
方法把任意对象序列化成一个str
,内容就是标准的JSON。然后,就可以把这个str
写入文件(Web开发时一般是服务器端将Python对象序列化成JSON后,直接将响应返回给客户端):
In [1]: import json In [2]: d = {'name': 'wangy', 'age': 18} In [3]: s = json.dumps(d) In [4]: s Out[4]: '{"name": "wangy", "age": 18}' In [5]: type(s) Out[5]: str In [6]: f = open('/tmp/test.json', 'w') In [7]: f.write(s) Out[7]: 28 In [8]: f.close()
[root@CentOS ~]# cat /tmp/test.json {"name": "wangy", "age": 18}
json.dump()
方法直接将对象序列化成JSON后写入一个file-like object
,Web开发中很少保存到文件,所以很少用到:
In [1]: import json In [2]: d = {'name': 'wangy', 'age': 18} In [3]: f = open('/tmp/test.json', 'w') In [4]: json.dump(d, f) In [5]: f.close()
json.loads()
将JSON反序列化为Python对象,Web开发中客户端发来的JSON数据由服务器接收后,在Python中表示为str
:
In [1]: import json In [2]: json_str = '{"name": "wangy", "age": 18}' In [3]: d = json.loads(json_str) In [4]: d Out[4]: {'name': 'wangy', 'age': 18} In [5]: type(d) Out[5]: dict
json.load()
方法从file-like object
中读取字符串并反序列化为Python对象,Web开发中一般都是直接从客户端接收JSON,所以很少用到:
In [1]: import json In [2]: f = open('/tmp/test.json', 'r') In [3]: d = json.load(f) In [4]: d Out[4]: {'name': 'wangy', 'age': 18}
json.dumps()
可以直接将Python中内置的基础数据类型(比如字典)直接序列化成JSON:
Python | JSON |
---|---|
dict | object |
list | array |
str | string |
int | number (int) |
float | number (real) |
True | true |
False | false |
None | null |
但是一般的类实例化后的对象使用json序列化会报错:
import json class User: def __init__(self, name, age): self.name = name self.age = age u = User('wangy', 18) print(json.dumps(u))
[root@CentOS ~]# python3 test.py Traceback (most recent call last): File "test.py", line 11, inprint(json.dumps(u)) File "/usr/local/python-3.6/lib/python3.6/json/__init__.py", line 231, in dumps return _default_encoder.encode(obj) File "/usr/local/python-3.6/lib/python3.6/json/encoder.py", line 199, in encode chunks = self.iterencode(o, _one_shot=True) File "/usr/local/python-3.6/lib/python3.6/json/encoder.py", line 257, in iterencode return _iterencode(o, 0) File "/usr/local/python-3.6/lib/python3.6/json/encoder.py", line 180, in default o.__class__.__name__) TypeError: Object of type 'User' is not JSON serializable
解决方法1: 如果只是简单地
将类属性序列化成JSON,可以使用__dict__
In [1]: import json In [2]: class User: ...: def __init__(self, name, age): ...: self.name = name ...: self.age = age ...: In [3]: u = User('wangy', 18) In [4]: json.dumps(u.__dict__) Out[4]: '{"name": "wangy", "age": 18}' In [5]: json.dumps(u, default=lambda obj: obj.__dict__) # 注意default的值,它也适配除了User类的其它类型 Out[5]: '{"name": "wangy", "age": 18}'
解决方法2: 一般不会原样输出类实例属性值,此时要指定json.dumps()
中default
的值为一个外部函数
import json class User: def __init__(self, name, age): self.name = name self.age = age def toJSON(self): data = {'name': self.name, 'age': self.age + 10} return data def my_json_encoder(obj): try: return obj.toJSON() except: return obj.__dict__ u = User('wangy', 18) print(json.dumps(u, default=my_json_encoder, indent=4))
[root@CentOS ~]# python3 test.py { "name": "wangy", "age": 28 }
如果要将JSON反序列化成类实例对象,使用
json.loads()
方法,并指定object_hook
参数值为自定义的函数
import json class User: def __init__(self, name, age): self.name = name self.age = age def toJSON(self): data = {'name': self.name, 'age': self.age + 10} return data def dict2user(d): return User(d['name'], d['age'] - 10) json_str = '{"name": "wangy", "age": 38}' u2 = json.loads(json_str, object_hook=dict2user) print(u2.name) print(u2.age)
[root@CentOS ~]# python3 test.py wangy 28