6.3.7 序列化模块 (pickle,json,shelve,xml)
文件写入,数据传输时,我们都是以字符串形式写入的(因为字符串可以encode成bytes)。
那其他类型(比如字典,列表等)想写入文件时如何处理呢?
这时候我们就需要一种操作,叫做序列化。
+那什么是序列化呢?
+ 把内存数据(unicode)转成字符(bytes类型),叫做序列化。
>序列化用于存入硬盘或者网络传输等
+ 把字符转成内存数据,叫反序列化。
简单点说,就是: 序列化: 其他类型数据 --> 字符类型
反序列化: 字符类型 --> 其他数据类型
>字符类型:str, bytes or bytearray
- 可以序列化,反序列化的模块有以下2种:
- pickle
json
这两种模块的用法完全一样
用eval()方法也可以达到效果。那有什么区别呢?
1. json模块 (.json文件)
序列化成字符串类型
- json特点
- 需要用open打开文件再处理
- 用'w','r'等类型打开文件进行
- 序列化成字符串类型
- 文件扩展名用.json
- json与pickle的区别:
- json能识别的数据类型:str,int,tuple,list,dict
- json可以跨平台使用
import json
json.dump(obj,fp,skipkeys,...)
将obj序列化成字符串类型,并写入文件对象fp中。返回Nonejson.dumps():将数据转成字符串并存到内存 这个一般用在:1.网络数据传输 2.跨平台,跨语言的数据交互
json.load(fp,cls,...)
读取json序列化的文件对象fp,反序列,返回反序列后的数据json.loads():读取dumps的数据,并反序列
import json
data = {'name': 'Alex', 'age': 22, 'salary': [1, 2, 3]}
# d1 = json.dumps(data) # 把data序列化。 ->序列化后转为str类型
# d2 = json.loads(data) # 把data反序列化。 ->反序列化转成相应数据类型(字典,列表等)
with open('test.json','w') as f: # 一般json序列化的文件扩展名用.json
d1 = json.dump(data,f) # 把data序列化,并且写入文件对象f中。因为返回None,所以d1是None
with open('test.json','r') as f:
d2 = json.load(f) # 把文件对象f的数据反序列化,返回给d2
拓展:数据交互方式有2种:json,xml json 可读性,数据大小 都优越于 xml
Azure的旧环境用的xml,新环境用的json
2. pickle模块 (.pkl文件)
序列化成bytes类型。
注意:因为序列化成bytes类型,所以文件的读写要用rb,wb等模式来进行!
- pickle特点
- 需要用open打开文件再处理
- 用'wb','rb'等类型打开文件进行
- 序列化成bytes类型
- 文件扩展名用.pkl
- pickle与json的区别:
pickle支持python里所有的数据类型(比如函数,类什么的都可以)
所以python使用的文件,不论内容时什么我们都可以pickle
pickle只能在python里使用
import pickle
pickle.dump(obj,file,protocol,...)
将obj序列化成bytes类型,并写入file文件,如果没有会创建文件。返回Nonepickle.load(file,...)
读取json序列化的文件对象fp,反序列,返回反序列后的数据.loads():读取dumps的数据,并反序列
import pickle
data = [1, 2, 3]
d1 = pickle.dumps(data) # 把data序列化。 ->序列化后转为bytes类型
d2 = pickle.loads(d1) # 把data反序列化。 ->反序列化转成相应数据类型
with open('test.pkl', 'wb') as f: # 一般pickle序列化的文件扩展名用.pkl
pickle.dump(data, f)
with open('test.json', 'rb') as f:
d = pickle.load(f)
print(type(d1))
print(type(d2))
print(d)
3. shelve模块 ()
shelve对pickle进行了封装,并且可以多次dump和load
- 区别及特点:
- pickle
- 一个文件只能dump一次(因为dump多次容易混乱)
- 用open()函数打开文件
- shelve
- 可以对文件多次dump,load
- 用shelve模块的.open()方法打开文件
- 数据的结构类似dict
- 可以直接用类似处理dict的方法处理数据
- pickle
序列化:
import shelve
f = shelve.open('shelve_test') # 打开(没有就创建)文件。会创建多个文件。可以跟open一样用with
list1 = ['a','b','c','d']
dict1 = {'key1':'abcd','key2':'1234'}
f['li1'] = list1 # 持久化列表 --> 就是写入序列化文件中,并且有个名字是'li1'
f['d1'] = dict1 # 持久化字典 --> 就是写入序列化文件中,并且有个名字是'd1'
f.close() # 关闭保存
反序列化:
import shelve
with shelve.open('shelve_test') as f:
print(f['li1']) # 直接调用li1的数据
print(f['d1']) # 直接调用d1的数据
# shelve打开的文件对象构造类似字典,可以使用字典的方法:
print('get li1:', f.get('li1')) # 有get()方法
i = f.items() # 有items()方法。 注意:这个i指向f.items()的内存地址,f.items()的值变化,i也会随之变化
print(id(i))
print(i) # 返回值是shelve对象类型,不能直接使用
print(list(i)) # 所以需要list()转换一下才可以看到内容
f['li2'] = ['aaaa','bbbb'] # 可以追加新的数据
print(list(i))
f.pop('li2') # 可以删除
print(list(i))
f['li1'] = [2, 3, 4, 5] # 可以直接修改某个数据的整个内容
print(list(i))
f['li1'][1] = 'b' # 但是不能改其中的一部分内容(字典可以)
print(list(i)) # 结果并没有改变
print(id(i))
4. xml模块 (.xml)
作用和json一样,是json出现之前很常用的序列化模块。 构造也和字典类似。用<>来定义数据。
读取xml文档内容:
# 读取数据 import xml.etree.ElementTree as ET tree = ET.parse('test.xml') # 打开xml文件,返回xml对象 root = tree.getroot() # 取数据最上级key # print(dir(root)) # 可以显示数据的keys print(root.tag) # 遍历文档 for child in root: print(child.tag, child.attrib) for i in child: print(i.tag, i.attrib) # 只遍历year节点 for node in root.iter('year'): print(node.tag, node.text)
修改和删除xml文档内容:
# 修改数据 import xml.etree.ElementTree as ET tree = ET.parse('test.xml') root = tree.getroot() # 取数据最上级key # 修改 for node in root.iter('year'): new_year = int(node.text) + 1 # 取数据并计算 node.text = str(new_year) # 把值转成str并赋值给原来的内容 node.set('update', 'yes') # 设置属性和值 tree.write('test.xml') # 内容写入文件
# 删除数据 import xml.etree.ElementTree as ET tree = ET.parse('test.xml') root = tree.getroot() # 取数据最上级key # 删除node for country in root.findall('country'): rank = int(country.find('rank').text) if rank > 50: root.remove(country) tree.write('output.xml') # 结果写到一个新文件里
创建xml文件
```python
# 创建xml文件
import xml.etree.ElementTree as ETroot = ET.Element('namelist') # 创建名为namelist的根(root)
name1 = ET.SubElement(root,'name',attrib={'enrolled':'yes'}) # 名称为'name'的数据放在'root'的节点上,属性设为{'enrolled':'yes'}
age = ET.SubElement(name1,'age',attrib={'checked':'no'}) # 名称为'age'的数据放在name1的节点上,属性设置为 {'checked':'no'}
sex = ET.SubElement(name1,'sex') # 名称为'sex'的数据放在name1的节点上
sex.text = '33' # sex的值设为'33'name2 = ET.SubElement(root,'name',attrib={'enrolled':'no'}) # 名称为'name'的数据放在'root'的节点上,属性设为{'enrolled':'yes'}
age = ET.SubElement(name2,'age') # 名称为'age'的数据放在name2的节点上
age.text = '19' # age的值设为'19'et = ET.ElementTree(root) # 生成文档对象
et.write('test.xml', encoding='utf-8', xml_declaration=True) # xml_declaration是要不要加版本号声明ET.dump(root)
```