在程序运行的过程当中,全部的变量都是在内存中,好比,定义一个dict:php
d = dict(name='Bob', age=20, score=88)
能够随时修改变量,好比把name
改为'Bill'
,可是一旦程序结束,变量所占用的内存就被操做系统所有回收。若是没有把修改后的'Bill'
存储到磁盘上,下次从新运行程序,变量又被初始化为'Bob'
。html
咱们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其余语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。python
序列化以后,就能够把序列化后的内容写入磁盘,或者经过网络传输到别的机器上。sql
反过来,把变量内容从序列化的对象从新读到内存里称之为反序列化,即unpickling。编程
Python提供两个模块来实现序列化:cPickle
和pickle
。这两个模块功能是同样的,区别在于cPickle
是C语言写的,速度快,pickle
是纯Python写的,速度慢,跟cStringIO
和StringIO
一个道理。用的时候,先尝试导入cPickle
,若是失败,再导入pickle
:json
try: import cPickle as pickle except ImportError: import pickle
首先,咱们尝试把一个对象序列化并写入文件:网络
>>> d = dict(name='Bob', age=20, score=88) >>> pickle.dumps(d) "(dp0\nS'age'\np1\nI20\nsS'score'\np2\nI88\nsS'name'\np3\nS'Bob'\np4\ns."
pickle.dumps()
方法把任意对象序列化成一个str,而后,就能够把这个str写入文件。或者用另外一个方法pickle.dump()
直接把对象序列化后写入一个file-like Object:编程语言
>>> f = open('dump.txt', 'wb') >>> pickle.dump(d, f) >>> f.close()
看看写入的dump.txt
文件,一堆乱七八糟的内容,这些都是Python保存的对象内部信息。函数
当咱们要把对象从磁盘读到内存时,能够先把内容读到一个str
,而后用pickle.loads()
方法反序列化出对象,也能够直接用pickle.load()
方法从一个file-like Object
中直接反序列化出对象。咱们打开另外一个Python命令行来反序列化刚才保存的对象:编码
>>> f = open('dump.txt', 'rb') >>> d = pickle.load(f) >>> f.close() >>> d {'age': 20, 'score': 88, 'name': 'Bob'}
变量的内容又回来了!
固然,这个变量和原来的变量是彻底不相干的对象,它们只是内容相同而已。
Pickle的问题和全部其余编程语言特有的序列化问题同样,就是它只能用于Python,而且可能不一样版本的Python彼此都不兼容,所以,只能用Pickle保存那些不重要的数据,不能成功地反序列化也不要紧。
JSON
若是咱们要在不一样的编程语言之间传递对象,就必须把对象序列化为标准格式,好比XML,但更好的方法是序列化为JSON,由于JSON表示出来就是一个字符串,能够被全部语言读取,也能够方便地存储到磁盘或者经过网络传输。JSON不只是标准格式,而且比XML更快,并且能够直接在Web页面中读取,很是方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应以下:
JSON类型 | Python类型 |
{} | dict |
[] | list |
"string" | 'str'或u'unicode' |
1234.56 | int或float |
true/false | True/False |
null | None |
Python内置的json
模块提供了很是完善的Python对象到JSON格式的转换。咱们先看看如何把Python对象变成一个JSON:
>>> import json >>> d = dict(name='Bob', age=20, score=88) >>> json.dumps(d) '{"age": 20, "score": 88, "name": "Bob"}'
dumps()
方法返回一个str
,内容就是标准的JSON。相似的,dump()
方法能够直接把JSON写入一个file-like Object
。
要把JSON反序列化为Python对象,用loads()
或者对应的load()
方法,前者把JSON的字符串反序列化,后者从file-like Object
中读取字符串并反序列化:
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}' >>> json.loads(json_str) {u'age': 20, u'score': 88, u'name': u'Bob'}
有一点须要注意,就是反序列化获得的全部字符串对象默认都是unicode
而不是str
。因为JSON标准规定JSON编码是UTF-8,因此咱们老是能正确地在Python的str
或unicode
与JSON的字符串之间转换。
JSON进阶
Python的dict
对象能够直接序列化为JSON的{}
,不过,不少时候,咱们更喜欢用class
表示对象,好比定义Student
类,而后序列化:
import json class Student(object): def __init__(self, name, age, score): self.name = name self.age = age self.score = score s = Student('Bob', 20, 88) print(json.dumps(s))
运行代码,绝不留情地获得一个TypeError
:
Traceback (most recent call last): ... TypeError: <__main__.Student object at 0x10aabef50> is not JSON serializable
错误的缘由是Student
对象不是一个可序列化为JSON的对象。
若是连class
的实例对象都没法序列化为JSON,这确定不合理!
别急,咱们仔细看看dumps()
方法的参数列表,能够发现,除了第一个必须的obj
参数外,dumps()
方法还提供了一大堆的可选参数:
https://docs.python.org/2/library/json.html#json.dumps
这些可选参数就是让咱们来定制JSON序列化。前面的代码之因此没法把Student
类实例序列化为JSON,是由于默认状况下,dumps()
方法不知道如何将Student
实例变为一个JSON的{}
对象。
可选参数default
就是把任意一个对象变成一个可序列为JSON的对象,咱们只须要为Student
专门写一个转换函数,再把函数传进去便可:
def student2dict(std): return { 'name': std.name, 'age': std.age, 'score': std.score } print(json.dumps(s, default=student2dict))
这样,Student
实例首先被student2dict()
函数转换成dict
,而后再被顺利序列化为JSON。
不过,下次若是遇到一个Teacher
类的实例,照样没法序列化为JSON。咱们能够偷个懒,把任意class
的实例变为dict
:
print(json.dumps(s, default=lambda obj: obj.__dict__))
由于一般class
的实例都有一个__dict__
属性,它就是一个dict
,用来存储实例变量。也有少数例外,好比定义了__slots__
的class。
一样的道理,若是咱们要把JSON反序列化为一个Student
对象实例,loads()
方法首先转换出一个dict
对象,而后,咱们传入的object_hook
函数负责把dict
转换为Student
实例:
def dict2student(d): return Student(d['name'], d['age'], d['score']) json_str = '{"age": 20, "score": 88, "name": "Bob"}' print(json.loads(json_str, object_hook=dict2student))
运行结果以下:
<__main__.Student object at 0x10cd3c190>
打印出的是反序列化的Student
实例对象。
小结
Python语言特定的序列化模块是pickle
,但若是要把序列化搞得更通用、更符合Web标准,就可使用json
模块。
json
模块的dumps()
和loads()
函数是定义得很是好的接口的典范。当咱们使用时,只须要传入一个必须的参数。可是,当默认的序列化或反序列机制不知足咱们的要求时,咱们又能够传入更多的参数来定制序列化或反序列化的规则,既作到了接口简单易用,又作到了充分的扩展性和灵活性。