Python 常用模块之json模块、pickle模块、configparser模块

57 阅读6分钟

一、json与pickle模块

json模块与pickle模块是用来给对象进行序列化的。

①__什么是序列化?

序列化指的是把内存里的数据类型转换成一种特定格式的内容,该格式的内容可以用于存储或传输给其他平台使用。

与之对应的是反序列化,顾名思义,就是把这种特定格式的内容反向转换成原来的数据类型。

  • 内存中的数据类型 ----> 序列化 ----> 特定的格式(json格式或者pickle格式)
  • 内存中的数据类型 <---- 反序列化 <---- 特定的格式(json格式或者pickle格式)

我们之前其实就使用过序列化与反序列化的思想:

{'aaa':111} ---> 序列化str({'aaa':111}) ---> "{'aaa':111}"
{'aaa':111} <--- 反序列化eval("{'aaa':111}") <--- "{'aaa':111}"

②__为什么要序列化?

序列化得到结果 ==> 特定的格式的内容有两种用途:

  1. 可用于存储 ==> 用于存档

  2. 传输给其他平台使用 ==> 跨平台数据交互

    如果我们要在不同的编程语言之间传递对象,比如Python的列表传送到Java环境或JavaScript环境,是不能被识别出来的。而我们就必须把对象序列化为一种大家都支持的中间格式,然后再进行转换。

针对用途1的特定格式:可以是一种专用的格式 ==》==pickle==(只有python可以识别)

针对用途2的特定格式:应该是一种通用的、能够被所有语言识别的格式 ==》==json==

==json表示的对象就是标准的JavaScript语言的对象==,JSON和Python内置的数据类型对应如下:

Json类型Python类型
{}dict
[]list
=="==string=="==str
1234.56int或float
true/falseTrue/False
nullNone

③__如何序列化与反序列化?(import json)

  • 序列化:json.dumps()

    import json
    
    res = json.dumps(True)
    print(res, type(res))  # true <class 'str'>
    ret = json.dumps([1, 'a', False])
    print(res, type(res))  # [1, "a", false] <class 'str'>
    
  • 反序列化:json.loads()

    # 接上一代码块
    
    l = json.loads(ret)
    print(l, type(l))  # [1, 'a', False] <class 'list'>
    

序列化后写入文件,与从文件读取json格式的字符串进行反序列化

序列化写入文件:
  1. 序列化的结果写入文件的复杂方法

    import json
    
    json_res = json.dumps([1, 'aaa', True, False])
    with open('test.json', mode='wt', encoding='utf-8') as f:
        f.write(json_res)
    
  2. 将序列化的结果写入文件的简单方法:json.dump()

    import json
    
    with open('test.json', mode='wt', encoding='utf-8') as f:
        json.dump([1, 'aaa', True, False], f)
    

test.json文件内容为:

[1, "aaa", true, false]
从文件读取json进行反序列化:
  1. 从文件读取json格式的字符串进行反序列化操作的复杂方法

    import json
    
    with open('test.json', mode='rt', encoding='utf-8') as f:
        json_res = f.read()
        l = json.loads(json_res)
        print(l, type(l))  # [1, 'aaa', True, False] <class 'list'>
    
  2. 从文件读取json格式的字符串进行反序列化操作的简单方法:json.load()

    import json
    
    with open('test.json', mode='rt', encoding='utf-8') as f:
        l = json.load(f)
        print(l, type(l))  # [1, 'aaa', True, False] <class 'list'>
    

强调:

  1. json格式兼容的是所有语言共同通用的数据类型,不能识别某一语言的所独有的类型

    import json
    
    # python独有的集合类型,放进去识别不出来,会报错
    json.dumps({1,2,3,4,5})
    
  2. 一定要搞清楚json格式,不要与python混淆,比如json的字符串格式只能是双引号。

    import json
    
    json.loads('[1, "a", false]')
    
  3. 在python解释器2.7与3.6之后都可以json.loads(bytes类型),但唯独3.5不可以。

    import json
    
    json.loads(b'{"a":111}')
    

④__猴子补丁

属性在运行时的动态替换,叫做猴子补丁(Monkey Patch)。==猴子补丁的核心就是用自己的代码替换所用模块的源代码==。

如果我们的程序中已经基于json模块编写了大量代码了,发现有一个模块ujson比它性能更高,但用法一样。我们肯定不想把所有代码里的json.dumps或json.loads都换成ujson.dumps或者ujson.loads,这样做太麻烦了。或者是把import json改成import ujson as json,这样做的话需要每个文件都重新导入一下,也太麻烦了。

那我们可能会想到这么做,直接在程序的入口文件中加上:

import json
import ujson

json.dumps = ujson.dumps
json.loads = ujson.loads

我们可以将这个代码封装成一个功能函数:

# 在入口处打猴子补丁
import json
import ujson


def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads


monkey_patch_json()  # 在入口文件处运行

之所以在入口处加,是因为模块在导入一次后,后续的导入便直接引用第一次导入的成果。

其实这种场景也比较多,比如我们引用团队通用库里的一个模块,又想丰富模块的功能,除了继承之外也可以考虑用MonkeyPatch。采用猴子补丁之后,如果发现ujson不符合预期,那也可以快速撤掉补丁。

但要注意MonkeyPatch带了便利的同时也有搞乱源代码的风险


⑤__pickle模块

使用pickle进行序列化与反序列化的方法在==形式==上和json是一样的。

序列化后写入文件(注意pickle这里使用wb),与从文件读取==pickle格式的byte数据==进行反序列化的使用方法,在==形式==上也和json一样。

import pickle

pickle.dumps()
pickle.loads()

pickle.dump()
pickle.load()

==pickle序列化的结果是byte类型==

import pickle

res = pickle.dumps({1, 2, 3})
print(res, type(res))
# b'\x80\x04\x95\x0b\x00\x00\x00\x00\x00\x00\x00\x8f\x94(K\x01K\x02K\x03\x90.' <class 'bytes'>

s = pickle.loads(res)
print(s, type(s))
# {1, 2, 3} <class 'set'>

pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python。

并且此方法Python2与Python3彼此都不兼容,因此,只能用pickle保存那些不重要的数据,不能成功地反序列化也没关系。


⑥__了解知识

XML模块

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单。xml协议在各个语言里都是支持的,在json还没诞生的黑暗年代,大家只能选择用xml。


二、configparser模块

configparser 是用来读取配置文件(.ini)的模块。

配置文件test.ini文件的内容如下:中括号“[ ]”内包含的为section,section 下面为类似于key-value 的配置内容。

# 注释符号1
; 注释符号2

[section1]
k1 = v1
k2 : v2
user = BOSS
age = 18
is_admin = true
IQ = 0

[section2]
k3 = v3

使用configparser 首先需要初始化实例,并读取配置文件:

import configparser

config = configparser.ConfigParser()
config.read('test.ini')
  1. 获取sections

    print(config.sections())
    # ['section1', 'section2']
    
  2. 获取某一section下的所有options

    print(config.options('section1'))
    # ['k1', 'k2', 'user', 'age', 'is_admin', 'iq']
    
  3. 获取某一section的items(所有配置项与对应信息)

    print(config.items('section1'))
    # [('k1', 'v1'), ('k2', 'v2'), ('user', 'BOSS'), ('age', '18'), ('is_admin', 'true'), ('iq', '0')]
    
  4. 获取指定的option对应值,默认都是str类型

    res = config.get('section1', 'user')
    print(res, type(res))
    # BOSS <class 'str'>
    
    res = config.getint('section1', 'age')  # 获取并转换成int类型
    print(res, type(res))
    # 18 <class 'int'>
    
    res = config.getboolean('section1', 'is_admin')  # 获取并转换成布尔类型
    print(res, type(res))
    # True <class 'bool'>
    
    res = config.getfloat('section1', 'iq')  # 获取并转换成float类型
    print(res, type(res))
    # 0.0 <class 'float'>