Python序列化模块 - pickle与json

Synopsis: Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。json模块的dumps()和loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性

1. pickle

Python程序运行时,对象都存在于内存中,一旦程序结束,这些对象所占用的内存就被操作系统全部回收。可以使用pickle模块将对象保存到磁盘文件中,这种将对象从内存中变成可存储或可传输的过程叫做序列化,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化(unpickling)

1.1 pickle.dumps()

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 ~]#

1.2 pickle.dump()

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()

1.3 pickle.loads()

可以先以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()

1.4 pickle.load()

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()

2. JSON

Python内置的json模块提供了非常完善的Python对象到JSON格式的转换

2.1 json.dumps()

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}

2.2 json.dump()

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()

2.3 json.loads()

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

2.4 json.load()

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}

2.5 TypeError: Object of type 'User' is not JSON serializable

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, in 
    print(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

你可能感兴趣的:(Python序列化模块 - pickle与json)