1. os模块(* * * *)
在自动化测试中,经常需要查找操作文件,比如说查找配置文件(从而读取配置文件的信息),查找测试报告(从而发送测试报告邮件),经常要对大量文件和大量路径进行操作,这就依赖于os模块,所以今天整理下比较常用的几个方法。网上这方面资料也很多,每次整理,只是对自己所学的知识进行梳理,从而加深对某个模块的使用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 1 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 2 2 os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd 3 3 os.curdir 返回当前目录: ('.') 4 4 os.pardir 获取当前目录的父目录字符串名:('..') 5 5 os.makedirs('dirname1/dirname2') 可生成多层递归目录 6 6 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 7 7 os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname 8 8 os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname 9 9 os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 10 10 os.remove() 删除一个文件 11 11 os.rename("oldname","newname") 重命名文件/目录 12 12 os.stat('path/filename') 获取文件/目录信息 13 13 os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" 14 14 os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" 15 15 os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为: 16 16 os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' 17 17 os.system("bash command") 运行shell命令,直接显示 18 18 os.environ 获取系统环境变量 19 19 os.path.abspath(path) 返回path规范化的绝对路径 20 20 os.path.split(path) 将path分割成目录和文件名二元组返回 21 21 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 22 22 os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 23 23 os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False 24 24 os.path.isabs(path) 如果path是绝对路径,返回True 25 25 os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False 26 26 os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False 27 27 os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 28 28 os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 29 29 os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
1.当前路径及路径下的文件
os.getcwd():查看当前所在路径。
os.listdir(path):列举目录下的所有文件。返回的是列表类型。
1 >>> import os 2 >>> os.getcwd() 3 'D:\\pythontest\\ostest' 4 >>> os.listdir(os.getcwd()) 5 ['hello.py', 'test.txt']
2.绝对路径
os.path.abspath(path):返回path的绝对路径。
1 >>> os.path.abspath('.') 2 'D:\\pythontest\\ostest' 3 >>> os.path.abspath('..') 4 'D:\\pythontest'
3.查看路径的文件夹部分和文件名部分
os.path.split(path):将路径分解为(文件夹,文件名),返回的是元组类型。可以看出,若路径字符串最后一个字符是\,则只有文件夹部分有值;若路径字符串中均无\,则只有文件名部分有值。若路径字符串有\,且不在最后,则文件夹和文件名均有值。且返回的文件夹的结果不包含\.
os.path.join(path1,path2,...):将path进行组合,若其中有绝对路径,则之前的path将被删除。
1 >>> os.path.split('D:\\pythontest\\ostest\\Hello.py') 2 ('D:\\pythontest\\ostest', 'Hello.py') 3 >>> os.path.split('.') 4 ('', '.') 5 >>> os.path.split('D:\\pythontest\\ostest\\') 6 ('D:\\pythontest\\ostest', '') 7 >>> os.path.split('D:\\pythontest\\ostest') 8 ('D:\\pythontest', 'ostest') 9 >>> os.path.join('D:\\pythontest', 'ostest') 10 'D:\\pythontest\\ostest' 11 >>> os.path.join('D:\\pythontest\\ostest', 'hello.py') 12 'D:\\pythontest\\ostest\\hello.py' 13 >>> os.path.join('D:\\pythontest\\b', 'D:\\pythontest\\a') 14 'D:\\pythontest\\a'
os.path.dirname(path):返回path中的文件夹部分,结果不包含'\'
1 >>> os.path.dirname('D:\\pythontest\\ostest\\hello.py') 2 'D:\\pythontest\\ostest' 3 >>> os.path.dirname('.') 4 '' 5 >>> os.path.dirname('D:\\pythontest\\ostest\\') 6 'D:\\pythontest\\ostest' 7 >>> os.path.dirname('D:\\pythontest\\ostest') 8 'D:\\pythontest'
os.path.basename(path):返回path中的文件名。
1 >>> os.path.basename('D:\\pythontest\\ostest\\hello.py') 2 'hello.py' 3 >>> os.path.basename('.') 4 '.' 5 >>> os.path.basename('D:\\pythontest\\ostest\\') 6 '' 7 >>> os.path.basename('D:\\pythontest\\ostest') 8 'ostest'
4.查看文件时间
os.path.getmtime(path):文件或文件夹的最后修改时间,从新纪元到访问时的秒数。
os.path.getatime(path):文件或文件夹的最后访问时间,从新纪元到访问时的秒数。
os.path.getctime(path):文件或文件夹的创建时间,从新纪元到访问时的秒数。
1 >>> os.path.getmtime('D:\\pythontest\\ostest\\hello.py') 2 1481695651.857048 3 >>> os.path.getatime('D:\\pythontest\\ostest\\hello.py') 4 1481687717.8506615 5 >>> os.path.getctime('D:\\pythontest\\ostest\\hello.py') 6 1481687717.8506615
5.查看文件大小
os.path.getsize(path):文件或文件夹的大小,若是文件夹返回0。
1 >>> os.path.getsize('D:\\pythontest\\ostest\\hello.py') 2 58L 3 >>> os.path.getsize('D:\\pythontest\\ostest') 4 0L
6.查看文件是否存在
os.path.exists(path):文件或文件夹是否存在,返回True 或 False。
1 >>> os.listdir(os.getcwd()) 2 ['hello.py', 'test.txt'] 3 >>> os.path.exists('D:\\pythontest\\ostest\\hello.py') 4 True 5 >>> os.path.exists('D:\\pythontest\\ostest\\Hello.py') 6 True 7 >>> os.path.exists('D:\\pythontest\\ostest\\Hello1.py') 8 False
7.一些表现形式参数
os中定义了一组文件、路径在不同操作系统中的表现形式参数,如:
1 >>> os.sep 2 '\\' 3 >>> os.extsep 4 '.' 5 >>> os.pathsep 6 ';' 7 >>> os.linesep 8 '\r\n'
8.实例说明
在自动化测试过程中,常常需要发送邮件,将最新的测试报告文档发送给相关人员查看,这是就需要查找最新文件的功能。
举例:查找文件夹下最新的文件。
代码如下:
1 import os 2 def new_file(test_dir): 3 #列举test_dir目录下的所有文件(名),结果以列表形式返回。 4 lists=os.listdir(test_dir) 5 #sort按key的关键字进行升序排序,lambda的入参fn为lists列表的元素,获取文件的最后修改时间,所以最终以文件时间从小到大排序 6 #最后对lists元素,按文件修改时间大小从小到大排序。 7 lists.sort(key=lambda fn:os.path.getmtime(test_dir+'\\'+fn)) 8 #获取最新文件的绝对路径,列表中最后一个值,文件夹+文件名 9 file_path=os.path.join(test_dir,lists[-1]) 10 return file_path 11 12 #返回D:\pythontest\ostest下面最新的文件 13 print new_file('D:\\system files\\workspace\\selenium\\email126pro\\email126\\report')
运行结果:
最后再啰嗦一句,关于lambda的用法(python中单行的最小函数):
1 key=lambda fn:os.path.getmtime(test_dir+'\\'+fn) 2 #相当于 3 def key(fn): 4 return os.path.getmtime(test_dir+'\\'+fn)
2.sys模块(* * *)
1.sys模块的常见函数列表
-
sys.argv
: 实现从程序外部向程序传递参数。 -
sys.exit([arg])
: 程序中间的退出,arg=0为正常退出。 -
sys.getdefaultencoding()
: 获取系统当前编码,一般默认为ascii。 -
sys.setdefaultencoding()
: 设置系统默认编码,执行dir(sys)时不会看到这个方法,在解释器中执行不通过,可以先执行reload(sys),在执行 setdefaultencoding('utf8'),此时将系统默认编码设置为utf8。(见设置系统默认编码 ) -
sys.getfilesystemencoding()
: 获取文件系统使用编码方式,Windows下返回'mbcs',mac下返回'utf-8'. -
sys.path
: 获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到。 -
sys.platform
: 获取当前系统平台。 -
sys.stdin,sys.stdout,sys.stderr
: stdin , stdout , 以及stderr 变量包含与标准I/O 流对应的流对象. 如果需要更好地控制输出,而print 不能满足你的要求, 它们就是你所需要的. 你也可以替换它们, 这时候你就可以重定向输出和输入到其它设备( device ), 或者以非标准的方式处理它们
2..sys.argv
功能:在外部向程序内部传递参数
示例:sys.py
1 #!/usr/bin/env python 2 3 import sys 4 print sys.argv[0] 5 print sys.argv[1]
输出:
# python sys.py argv1 sys.py argv1
3.sys.exit(n)
功能:执行到主程序末尾,解释器自动退出,但是如果需要中途退出程序,可以调用sys.exit函数,带有一个可选的整数参数返回给调用它的程序,表示你可以在主程序中捕获对sys.exit的调用。(0是正常退出,其他为异常)
示例:exit.py
1 #!/usr/bin/env python 2 3 import sys 4 5 def exitfunc(value): 6 print value 7 sys.exit(0) 8 9 print "hello" 10 11 try: 12 sys.exit(1) 13 except SystemExit,value: 14 exitfunc(value) 15 16 print "come?"
输出:
# python exit.py hello 1
4.sys.path
功能:获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到。
示例:
1 >>> import sys 2 >>> sys.path 3 ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PILcompat', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
sys.path.append("自定义模块路径")
5.sys.modules
功能:sys.modules
是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules
将自动记录该模块。当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。它拥有字典所拥有的一切方法。
示例:modules.py
1 #!/usr/bin/env python 2 3 import sys 4 5 print sys.modules.keys() 6 7 print sys.modules.values() 8 9 print sys.modules["os"]
输出:
python modules.py ['copy_reg', 'sre_compile', '_sre', 'encodings', 'site', '__builtin__',......
6.sys.stdin\stdout\stderr
功能:stdin , stdout , 以及stderr 变量包含与标准I/O 流对应的流对象. 如果需要更好地控制输出,而print 不能满足你的要求, 它们就是你所需要的. 你也可以替换它们, 这时候你就可以重定向输出和输入到其它设备( device ), 或者以非标准的方式处理它们
4.json & pickle(* * * *)
一、前言
1. 现实需求
每种编程语言都有各自的数据类型,其中面向对象的编程语言还允许开发者自定义数据类型(如:自定义类),Python也是一样。很多时候我们会有这样的需求:
- 把内存中的各种数据类型的数据通过网络传送给其它机器或客户端;
- 把内存中的各种数据类型的数据保存到本地磁盘持久化;
2.数据格式
如果要将一个系统内的数据通过网络传输给其它系统或客户端,我们通常都需要先把这些数据转化为字符串或字节串,而且需要规定一种统一的数据格式才能让数据接收端正确解析并理解这些数据的含义。XML 是早期被广泛使用的数据交换格式,在早期的系统集成论文中经常可以看到它的身影;如今大家使用更多的数据交换格式是JSON(JavaScript Object Notation),它是一种轻量级的数据交换格式。JSON相对于XML而言,更加加单、易于阅读和编写,同时也易于机器解析和生成。除此之外,我们也可以自定义内部使用的数据交换格式。
如果是想把数据持久化到本地磁盘,这部分数据通常只是供系统内部使用,因此数据转换协议以及转换后的数据格式也就不要求是标准、统一的,只要本系统内部能够正确识别即可。但是,系统内部的转换协议通常会随着编程语言版本的升级而发生变化(改进算法、提高效率),因此通常会涉及转换协议与编程语言的版本兼容问题,下面要时候的pickle协议就是这样一个例子。
3. 序列化/反序列化
将对象转换为可通过网络传输或可以存储到本地磁盘的数据格式(如:XML、JSON或特定格式的字节串)的过程称为序列化;反之,则称为反序列化。
4.相关模块
本节要介绍的就是Python内置的几个用于进行数据序列化的模块:
之前我们学习过用eval内置方法可以将一个字符串转成python对象,不过,eval方法是有局限性的,对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval的重点还是通常用来执行一个字符串表达式,并返回表达式的值。
模块名称 | 描述 | 提供的api |
---|---|---|
json | 用于实现Python数据类型与通用(json)字符串之间的转换 | dumps()、dump()、loads()、load() |
pickle | 用于实现Python数据类型与Python特定二进制格式之间的转换 | dumps()、dump()、loads()、load() |
shelve | 专门用于将Python数据类型的持久化到磁盘,shelf是一个类似dict的对象,操作十分便捷 | open() |
二、json模块
大部分编程语言都会提供处理json数据的接口,Python 2.6开始加入了json模块,且把它作为一个内置模块提供,无需下载即可使用。
1. 序列化与反序列化
Python的JSON模块 序列化与反序列化的过程分别叫做:encoding 和 decoding。
- encoding: 把Python对象转换成JSON字符串
- decoding: 把JSON字符串转换成python对象
json模块提供了以下两个方法来进行序列化和反序列化操作:
1 # 序列化:将Python对象转换成json字符串 2 dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw) 3 4 # 反序列化:将json字符串转换成Python对象 5 loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
除此之外,json模块还提供了两个额外的方法允许我们直接将序列化后得到的json数据保存到文件中,以及直接读取文件中的json数据进行反序列化操作:
1 # 序列化:将Python对象转换成json字符串并存储到文件中 2 dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, indent=None, separators=None, default=None, sort_keys=False, **kw) 3 4 # 反序列化:读取指定文件中的json字符串并转换成Python对象 5 load(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
2. JSON与Python之间数据类型对应关系
Python转JSON
Python | JSON |
---|---|
dict | Object |
list, tuple | array |
str | string |
int, float, int- & float-derived Enums | numbers |
True | true |
False | false |
None | null |
JSON转Python
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number(int) | int |
number(real) | float |
true | True |
false | False |
null | None |
说明:
- Python dict中的非字符串key被转换成JSON字符串时都会被转换为小写字符串;
- Python中的tuple,在序列化时会被转换为array,但是反序列化时,array会被转化为list;
- 由以上两点可知,当Python对象中包含tuple数据或者包含dict,且dict中存在非字符串的key时,反序列化后得到的结果与原来的Python对象是不一致的;
- 对于Python内置的数据类型(如:str, unicode, int, float, bool, None, list, tuple, dict)json模块可以直接进行序列化/反序列化处理;对于自定义类的对象进行序列化和反序列化时,需要我们自己定义一个方法来完成定义object和dict之间进行转化。
3. 实例:内置数据类型序列化/反序列化
序列化
1 # 序列化 2 >>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}) 3 '{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}'
sort_keys参数:表示序列化时是否对dict的key进行排序(dict默认是无序的)
1 # 序列化并对key进行排序 2 >>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, sort_keys=True) 3 '{"a": "str", "b": 11.1, "c": true, "d": null, "e": 10, "f": [1, 2, 3], "g": [4, 5, 6]}'
indent参数: 表示缩进的意思,它可以使得数据存储的格式变得更加优雅、可读性更强;如果indent是一个非负整数或字符串,则JSON array元素和object成员将会被以相应的缩进级别进行打印输出;如果indent是0或负数或空字符串,则将只会插入换行,不会有缩进。
1 # 序列化并对key进行排序及格式化输出 2 >>> print(json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, sort_keys=True, indent=4)) 3 { 4 "a": "str", 5 "b": 11.1, 6 "c": true, 7 "d": null, 8 "e": 10, 9 "f": [ 10 1, 11 2, 12 3 13 ], 14 "g": [ 15 4, 16 5, 17 6 18 ] 19 }
separators参数:尽管indent参数可以使得数据存储的格式变得更加优雅、可读性更强,但是那是通过添加一些冗余的空白字符进行填充的。当json被用于网络数据通信时,应该尽可能的减少无用的数据传输,这样可以节省贷款并加快数据传输速度。json模块序列化Python对象后得到的json字符串中的','号和':'号分隔符后默认都会附加一个空白字符,我们可以通过separators参数重新指定分隔符,从而去除无用的空白字符;
- 该参数的值应该是一个tuple(item_separator, key_separator)
- 如果indent是None,其默认值为(', ', ': ')
- 如果indent不为None,则默认值为(',', ': ')
- 我们可以通过为separator赋值为(',', ':')来消除空白字符
1 >>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}) 2 '{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}' 3 4 >>> json.dumps({'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, separators=(',',':')) 5 '{"a":"str","c":true,"b":11.1,"e":10,"d":null,"g":[4,5,6],"f":[1,2,3]}'
反序列化
1 # 反序列化 2 >>> json.loads('{"a": "str", "c": true, "b": 11.1, "e": 10, "d": null, "g": [4, 5, 6], "f": [1, 2, 3]}') 3 {'c': True, 'e': 10, 'a': 'str', 'g': [4, 5, 6], 'd': None, 'f': [1, 2, 3], 'b': 11.1} 4 5 >>> json.loads('{"a":"str","c":true,"b":11.1,"e":10,"d":null,"g":[4,5,6],"f":[1,2,3]}') 6 {'c': True, 'e': 10, 'a': 'str', 'g': [4, 5, 6], 'd': None, 'f': [1, 2, 3], 'b': 11.1}
dump()与load()函数示例
1 # 序列化到文件中 2 >>> with open('test.json', 'w') as fp: 3 ... json.dump({'a':'str中国', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}, fp, indent=4) 4 5 # 反序列化文件中的内容 6 >>> with open('test.json', 'r') as fp: 7 ... json.load(fp) 8 {'e': 10, 'g': [4, 5, 6], 'b': 11.1, 'c': True, 'd': None, 'a': 'str中国', 'f': [1, 2, 3]}
需要说明的是: 如果试图使用相同的fp重复调用dump()函数去序列化多个对象(或序列化同一个对象多次),将会产生一个无效的JSON文件,也就是说对于一个fp只能调用一次dump()。
4. 实例:自定义数据类型的序列化/反序列化
Python是面向对象的编程语言,我们可以自定义需要的数据类型;实际工作中,我们常常会用到自定义数据类型的序列化与反序列化操作。要实现自定义数据类型的序列化与反序列化有两种方式:
- 通过转换函数实现
- 通过继承JSONEncoder和JSONDecoder类实现
首先来自定义一个数据类型
1 class Student(object): 2 def __init__(self, name, age, sno): 3 self.name = name 4 self.age = age 5 self.sno = sno 6 7 def __repr__(self): 8 return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)
直接调用dumps()方法会引发TypeError错误:
1 >>> stu = Student('Tom', 19, 1) 2 >>> print(stu) 3 Student [name: Tom, age: 19, sno: 1] 4 >>> 5 >>> json.dumps(stu) 6 ... 7 TypeError: Student [name: Tom, age: 19, sno: 1] is not JSON serializable
上面的异常信息中指出:stu对象不可以被序列化为JSON个数的数据。那么我们分别通过“编写转换函数” 和 “继承JSONEncoder和JSONDecoder类” 来实现对这个自定义数据类型的JSON序列化和反序列化。
方法1:编写转换函数
那么这个转换函数要完成哪两个数据类型之间的转换呢? 从上面列出的JSON与Python数据类型的对应表中可知,JSON中的object对应的是Python中的dict,因此要对Python中的自定义数据类型的对象进行序列化,就需要先把这个对象转换成json模块可以直接进行序列化dict类型。由此可知,这个转换函数是要完成的是Python对象(不是JSON对象)与dict之间的相互转换,且序列化时转换过程是“Python对象 --> dict --> JSON object”,反序列化的过程是“JSON object -> dict --> Python对象”。所以,我们需要编写两个转换函数来分别实现序列化和反序列化时的转换过程。
1 def obj2dict(obj): 2 d = {} 3 d['__class__'] = obj.__class__.__name__ 4 d['__module__'] = obj.__module__ 5 d.update(obj.__dict__) 6 return d 7 8 def dict2obj(d): 9 if '__class__' in d: 10 class_name = d.pop('__class__') 11 module_name = d.pop('__module__') 12 module = __import__(module_name) 13 class_ = getattr(module, class_name) 14 args = dict((key.encode('ascii'), value) for key, value in d.items()) 15 instance = class_(**args) 16 else: 17 instance = d 18 return instance
继承JSONEncoder实现反序列化时还有一个额外的作用,就是可以通过iterencode()方法把一个很大的数据对象分多次进行序列化,这对于网络传输、磁盘持久化等情景非常有用。
1 >>> for chunk in MyJSONEncoder().iterencode(stu): 2 ... print(chunk) 3 ... 4 { 5 "__class__" 6 : 7 "Student" 8 , 9 "name" 10 : 11 "Tom" 12 , 13 "__module__" 14 : 15 "__main__" 16 , 17 "sno" 18 : 19 1 20 , 21 "age" 22 : 23 19 24 }
大数据对象序列化网络传输伪代码:
1 for chunk in JSONEncoder().iterencode(bigobject): 2 mysocket.write(chunk)
序列化测试:
1 >>> import json 2 3 >>> obj2dict(stu) 4 {'sno': 1, '__module__': '__main__', 'age': 19, '__class__': 'Student', 'name': 'Tom'} 5 6 >>> json.dumps(obj2dict(stu)) 7 '{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}' 8 9 >>> json.dumps(stu, default=obj2dict) 10 '{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}'
json.dumps(stu, default=obj2dict)
等价于 json.dumps(obj2dict(stu))
反序列化测试:
1 >>> json.loads('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}') 2 {u'sno': 1, u'__module__': u'__main__', u'age': 19, u'name': u'Tom', u'__class__': u'Student'} 3 4 >>> dict2obj(json.loads('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}')) 5 Student [name: Tom, age: 19, sno: 1] 6 7 >>> json.loads('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}', object_hook=dict2obj) 8 Student [name: Tom, age: 19, sno: 1]
json.loads(JSON_STR, object_hook=dict2obj)
等价于 dict2obj(json.loads(JSON_STR))
方法2:
继承JSONEncoder和JSONDecoder实现子类
1 import json 2 3 class MyJSONEncoder(json.JSONEncoder): 4 def default(self, obj): 5 d = {} 6 d['__class__'] = obj.__class__.__name__ 7 d['__module__'] = obj.__module__ 8 d.update(obj.__dict__) 9 return d 10 11 class MyJSONDecoder(json.JSONDecoder): 12 def __init__(self): 13 json.JSONDecoder.__init__(self, object_hook=self.dict2obj) 14 15 def dict2obj(self, d): 16 if '__class__' in d: 17 class_name = d.pop('__class__') 18 module_name = d.pop('__module__') 19 module = __import__(module_name) 20 class_ = getattr(module, class_name) 21 args = dict((key.encode('ascii'), value) for key, value in d.items()) 22 instance = class_(**args) 23 else: 24 instance = d 25 return instance
序列化测试:
1 >>> stu = Student('Tom', 19, 1) 2 3 # 方式一:直接调用子类MyJSONEncoder的encode()方法进行序列化 4 >>> MyJSONEncoder().encode(stu) 5 '{"__class__": "Student", "__module__": "__main__", "name": "Tom", "age": 19, "sno": 1}' 6 >>> MyJSONEncoder(separators=(',', ':')).encode(stu) 7 '{"__class__":"Student","__module__":"__main__","name":"Tom","age":19,"sno":1}' 8 9 # 方式二:将子类MyJSONEncoder作为cls参数的值传递给json.dumps()函数 10 >>> json.dumps(stu, cls=MyJSONEncoder) 11 '{"__class__": "Student", "__module__": "__main__", "name": "Tom", "age": 19, "sno": 1}' 12 >>> json.dumps(stu, cls=MyJSONEncoder, separators=(',', ':')) 13 '{"__class__":"Student","__module__":"__main__","name":"Tom","age":19,"sno":1}'
反序列化测试:
1 >>> MyJSONDecoder().decode('{"sno": 1, "__module__": "__main__", "age": 19, "__class__": "Student", "name": "Tom"}') 2 Student [name: Tom, age: 19, sno: 1]
说明: 经过测试发现MyJSONDecoder().decode(JSON_STR)
和 json.loads(JSON_STR, object_hook=dict2obj)
只能在Python 2.7上正确执行,在Python 3.5上无法正确执行;而 json.loads(JSON_STR, cls=MyJSONDecoder)
无论在Python 2.7还是在Python 3.5上都无法正确执行。这说明json模块对于自定义数据类型的反序列化支持还是比较有限的,但是我们也可以通过json.loads(JSON_STR)函数,不指定cls参数来得到一个dict对象,然后自己完成dict到object的转换。
三、pickle模块
pickle模块实现了用于对Python对象结构进行 序列化 和 反序列化 的二进制协议,与json模块不同的是pickle模块序列化和反序列化的过程分别叫做 pickling 和 unpickling:
- pickling: 是将Python对象转换为字节流的过程;
- unpickling: 是将字节流二进制文件或字节对象转换回Python对象的过程;
1. pickle模块与json模块对比
- JSON是一种文本序列化格式(它输出的是unicode文件,大多数时候会被编码为utf-8),而pickle是一个二进制序列化格式;
- JOSN是我们可以读懂的数据格式,而pickle是二进制格式,我们无法读懂;
- JSON是与特定的编程语言或系统无关的,且它在Python生态系统之外被广泛使用,而pickle使用的数据格式是特定于Python的;
- 默认情况下,JSON只能表示Python内建数据类型,对于自定义数据类型需要一些额外的工作来完成;pickle可以直接表示大量的Python数据类型,包括自定数据类型(其中,许多是通过巧妙地使用Python内省功能自动实现的;复杂的情况可以通过实现specific object API来解决)
2. pickle模块使用的数据流格式
上面提到,pickle使用的数据格式是特定于Python的。这使得它不受诸如JSON或XDR的外部标准限值,但是这也意味着非Python程序可能无法重建pickled Python对象。默认情况下,pickle数据格式使用相对紧凑的二进制表示。如果需要最佳大小特征,可以有效的压缩pickled数据。pickletools模块包含可以用于对pickle生成的数据流进行分析的工具。目前有5种不同的协议可以用于pickle。使用的协议越高,就需要更新的Python版本去读取pickle产生的数据:
- 协议v0是原始的“人类可读”协议,并且向后倩蓉早期的Python版本;
- 协议v1是一个旧的二进制格式,也与早期版本的Python兼容;
- 协议v2在Python 2.3中引入,它提供更高效的pickling;
- 协议v3是在Python 3.0添加的协议,它明确支持bytes对象,且不能被Python 2.x 进行unpickle操作;这是默认协议,也是当需要兼容其他Python 3版本时被推荐使用的协议;
- 协议4是在Python 3.4添加的协议,它添加了对极大对象的支持,pickling更多种类的对象,以及一些数据格式的优化。
说明: Python 2.x中默认使用的是协议v0,如果协议指定为赋值或HIGHEST_PROTOCOL,将使用当前可用的最高协议版本;Python 3.x中默认使用的是协议v3,它兼容其他Python 3版本,但是不兼容Python 2。
注意: 序列化(Serialization)是一个比持久化(Persistence)更加原始的概念;虽然pickle
可以读写文件对象,但是它不处理持久化对象的命名问题,也不处理对持久化对象的并发访问问题(甚至更复杂的问题)。pickle
模块可以将复杂对象转换为字节流,并且可以将字节流转换为具有相同内部结构的对象。或许最可能对这些字节流做的事情是将它们写入文件,但是也可以对它们进行网络传输或将它们存储在数据库中。shelve
模块提供了一个简单的接口用于在DBM风格的数据库文件上对对象进行pickle和unpickle操作。
3. pickle模块提供的相关函数
pickle模块提供的几个序列化/反序列化的函数与json模块基本一致:
1 # 将指定的Python对象通过pickle序列化作为bytes对象返回,而不是将其写入文件 2 dumps(obj, protocol=None, *, fix_imports=True) 3 4 # 将通过pickle序列化后得到的字节对象进行反序列化,转换为Python对象并返回 5 loads(bytes_object, *, fix_imports=True, encoding="ASCII", errors="strict") 6 7 # 将指定的Python对象通过pickle序列化后写入打开的文件对象中,等价于`Pickler(file, protocol).dump(obj)` 8 dump(obj, file, protocol=None, *, fix_imports=True) 9 10 # 从打开的文件对象中读取pickled对象表现形式并返回通过pickle反序列化后得到的Python对象 11 load(file, *, fix_imports=True, encoding="ASCII", errors="strict")
说明: 上面这几个方法参数中,*号后面的参数都是Python 3.x新增的,目的是为了兼容Python 2.x,具体用法请参看官方文档。
4. 实例:内置数据类型的序列化/反序列化
1 >>> import pickle 2 >>> 3 >>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)} 4 5 # 序列化 6 >>> var_b = pickle.dumps(var_a) 7 >>> var_b 8 b'\x80\x03}q\x00(X\x01\x00\x00\x00eq\x01K\nX\x01\x00\x00\x00aq\x02X\x03\x00\x00\x00strq\x03X\x01\x00\x00\x00fq\x04]q\x05(K\x01K\x02K\x03eX\x01\x00\x00\x00gq\x06K\x04K\x05K\x06\x87q\x07X\x01\x00\x00\x00bq\x08G@&333333X\x01\x00\x00\x00cq\t\x88X\x01\x00\x00\x00dq\nNu.' 9 10 # 反序列化 11 >>> var_c = pickle.loads(var_b) 12 >>> var_c 13 {'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}
dump()与load()
1 >>> import pickle 2 >>> 3 >>> var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)} 4 5 # 持久化到文件 6 >>> with open('pickle.txt', 'wb') as f: 7 ... pickle.dump(var_a, f) 8 ... 9 10 # 从文件中读取数据 11 >>> with open('pickle.txt', 'rb') as f: 12 ... var_b = pickle.load(f) 13 ... 14 >>> var_b 15 {'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None} 16 >>>
说明:
- 默认情况下Python 2.x中pickled后的数据是字符串形式,需要将它转换为字节对象才能被Python 3.x中的pickle.loads()反序列化;Python 3.x中pickling所使用的协议是v3,因此需要在调用pickle.dumps()时指定可选参数protocol为Python 2.x所支持的协议版本(0,1,2),否则pickled后的数据不能被被Python 2.x中的pickle.loads()反序列化;
- Python 3.x中pickle.dump()和pickle.load()方法中指定的文件对象,必须以二进制模式打开,而Python 2.x中可以以二进制模式打开,也可以以文本模式打开。
5. 实例:自定义数据类型的序列化/反序列化
首先来自定义一个数据类型:
1 class Student(object): 2 def __init__(self, name, age, sno): 3 self.name = name 4 self.age = age 5 self.sno = sno 6 7 def __repr__(self): 8 return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno)
pickle模块可以直接对自定数据类型进行序列化/反序列化操作,无需编写额外的处理函数或类。
1 >>> stu = Student('Tom', 19, 1) 2 >>> print(stu) 3 Student [name: Tom, age: 19, sno: 1] 4 5 # 序列化 6 >>> var_b = pickle.dumps(stu) 7 >>> var_b 8 b'\x80\x03c__main__\nStudent\nq\x00)\x81q\x01}q\x02(X\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Tomq\x04X\x03\x00\x00\x00ageq\x05K\x13X\x03\x00\x00\x00snoq\x06K\x01ub.' 9 10 # 反序列化 11 >>> var_c = pickle.loads(var_b) 12 >>> var_c 13 Student [name: Tom, age: 19, sno: 1] 14 15 # 持久化到文件 16 >>> with open('pickle.txt', 'wb') as f: 17 ... pickle.dump(stu, f) 18 ... 19 20 # 从文件总读取数据 21 >>> with open('pickle.txt', 'rb') as f: 22 ... pickle.load(f) 23 ... 24 Student [name: Tom, age: 19, sno: 1]
四、shelve模块
shelve是一个简单的数据存储方案,类似key-value数据库,可以很方便的保存python对象,其内部是通过pickle协议来实现数据序列化。shelve只有一个open()函数,这个函数用于打开指定的文件(一个持久的字典),然后返回一个shelf对象。shelf是一种持久的、类似字典的对象。它与“dbm”的不同之处在于,其values值可以是任意基本Python对象--pickle模块可以处理的任何数据。这包括大多数类实例、递归数据类型和包含很多共享子对象的对象。keys还是普通的字符串。
open(filename, flag='c', protocol=None, writeback=False)
flag 参数表示打开数据存储文件的格式,可取值与dbm.open()
函数一致:
值 | 描述 |
---|---|
'r' | 以只读模式打开一个已经存在的数据存储文件 |
'w' | 以读写模式打开一个已经存在的数据存储文件 |
'c' | 以读写模式打开一个数据存储文件,如果不存在则创建 |
'n' | 总是创建一个新的、空数据存储文件,并以读写模式打开 |
protocol 参数表示序列化数据所使用的协议版本,默认是pickle v3;
writeback 参数表示是否开启回写功能。
我们可以把shelf对象当dict来使用--存储、更改、查询某个key对应的数据,当操作完成之后,调用shelf对象的close()函数即可。当然,也可以使用上下文管理器(with语句),避免每次都要手动调用close()方法。
实例:
1 # 保存数据 2 with shelve.open('student') as db: 3 db['name'] = 'Tom' 4 db['age'] = 19 5 db['hobby'] = ['篮球', '看电影', '弹吉他'] 6 db['other_info'] = {'sno': 1, 'addr': 'xxxx'} 7 8 # 读取数据 9 with shelve.open('student') as db: 10 for key,value in db.items(): 11 print(key, ': ', value)
输出:
name : Tom age : 19 hobby : ['篮球', '看电影', '弹吉他'] other_info : {'sno': 1, 'addr': 'xxxx'}
实例:自定义数据类型操作
1 # 自定义class 2 class Student(object): 3 def __init__(self, name, age, sno): 4 self.name = name 5 self.age = age 6 self.sno = sno 7 8 def __repr__(self): 9 return 'Student [name: %s, age: %d, sno: %d]' % (self.name, self.age, self.sno) 10 11 # 保存数据 12 tom = Student('Tom', 19, 1) 13 jerry = Student('Jerry', 17, 2) 14 15 with shelve.open("stu.db") as db: 16 db['Tom'] = tom 17 db['Jerry'] = jerry 18 19 # 读取数据 20 with shelve.open("stu.db") as db: 21 print(db['Tom']) 22 print(db['Jerry'])
输出:
Student [name: Tom, age: 19, sno: 1]
Student [name: Jerry, age: 17, sno: 2]
五、总结
1. 对比
json模块常用于编写web接口,将Python数据转换为通用的json格式传递给其它系统或客户端;也可以用于将Python数据保存到本地文件中,缺点是明文保存,保密性差。另外,如果需要保存费内置数据类型需要编写额外的转换函数或自定义类。
pickle模块和shelve模块由于使用其特有的序列化协议,其序列化之后的数据只能被Python识别,因此只能用于Python系统内部。另外,Python 2.x 和 Python
3.x 默认使用的序列化协议也不同,如果需要互相兼容需要在序列化时通过protocol参数指定协议版本。除了上面这些缺点外,pickle模块和shelve模块相对于json模块的优点在于对于自定义数据类型可以直接序列化和反序列化,不需要编写额外的转换函数或类。
shelve模块可以看做是pickle模块的升级版,因为shelve使用的就是pickle的序列化协议,但是shelve比pickle提供的操作方式更加简单、方便。shelve模块相对于其它两个模块在将Python数据持久化到本地磁盘时有一个很明显的优点就是,它允许我们可以像操作dict一样操作被序列化的数据,而不必一次性的保存或读取所有数据。
2. 建议
- 需要与外部系统交互时用json模块;
- 需要将少量、简单Python数据持久化到本地磁盘文件时可以考虑用pickle模块;
- 需要将大量Python数据持久化到本地磁盘文件或需要一些简单的类似数据库的增删改查功能时,可以考虑用shelve模块。
3. 附录
要实现的功能 | 可以使用的api |
---|---|
将Python数据类型转换为(json)字符串 | json.dumps() |
将json字符串转换为Python数据类型 | json.loads() |
将Python数据类型以json形式保存到本地磁盘 | json.dump() |
将本地磁盘文件中的json数据转换为Python数据类型 | json.load() |
将Python数据类型转换为Python特定的二进制格式 | pickle.dumps() |
将Python特定的的二进制格式数据转换为Python数据类型 | pickle.loads() |
将Python数据类型以Python特定的二进制格式保存到本地磁盘 | pickle.dump() |
将本地磁盘文件中的Python特定的二进制格式数据转换为Python数据类型 | pickle.load() |
以类型dict的形式将Python数据类型保存到本地磁盘或读取本地磁盘数据并转换为数据类型 | shelve.open() |
参考:http://www.jb51.net/article/109997.htm
5.xml模块(* *)
xml即可扩展标记语言,它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。从结构上,很像HTML超文本标记语言。但他们被设计的目的是不同的,超文本标记语言被设计用来显示数据,其焦点是数据的外观。它被设计用来传输和存储数据,其焦点是数据的内容。那么Python是如何处理XML语言文件的呢?下面一起来看看Python常用内置模块之xml模块吧。
本文主要学习的ElementTree是python的XML处理模块,它提供了一个轻量级的对象模型。在使用ElementTree模块时,需要import xml.etree.ElementTree的操作。ElementTree表示整个XML节点树,而Element表示节点数中的一个单独的节点。
1、解析XML
1 from xml.etree import ElementTree as ET 2 3 4 # 打开文件,读取XML内容 5 str_xml = open('xo.xml', 'r').read() 6 7 # 将字符串解析成xml特殊对象,root代指xml文件的根节点 8 root = ET.XML(str_xml) 9 10 11 利用ElementTree.XML将字符串解析成xml对象
from xml.etree import ElementTree as ET # 直接解析xml文件 tree = ET.parse("xo.xml") # 获取xml文件的根节点 root = tree.getroot() 利用ElementTree.parse将文件直接解析成xml对象
2、操作XML
XML格式类型是节点嵌套节点,对于每一个节点均有以下功能,以便对当前节点进行操作:
1.节点功能一览表
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Element: 2 """An XML element. 3 4 This class is the reference implementation of the Element interface. 5 6 An element's length is its number of subelements. That means if you 7 want to check if an element is truly empty, you should check BOTH 8 its length AND its text attribute. 9 10 The element tag, attribute names, and attribute values can be either 11 bytes or strings. 12 13 *tag* is the element name. *attrib* is an optional dictionary containing 14 element attributes. *extra* are additional element attributes given as 15 keyword arguments. 16 17 Example form: 18 <tag attrib>text<child/>...</tag>tail 19 20 """ 21 22 当前节点的标签名 23 tag = None 24 """The element's name.""" 25 26 当前节点的属性 27 28 attrib = None 29 """Dictionary of the element's attributes.""" 30 31 当前节点的内容 32 text = None 33 """ 34 Text before first subelement. This is either a string or the value None. 35 Note that if there is no text, this attribute may be either 36 None or the empty string, depending on the parser. 37 38 """ 39 40 tail = None 41 """ 42 Text after this element's end tag, but before the next sibling element's 43 start tag. This is either a string or the value None. Note that if there 44 was no text, this attribute may be either None or an empty string, 45 depending on the parser. 46 47 """ 48 49 def __init__(self, tag, attrib={}, **extra): 50 if not isinstance(attrib, dict): 51 raise TypeError("attrib must be dict, not %s" % ( 52 attrib.__class__.__name__,)) 53 attrib = attrib.copy() 54 attrib.update(extra) 55 self.tag = tag 56 self.attrib = attrib 57 self._children = [] 58 59 def __repr__(self): 60 return "<%s %r at %#x>" % (self.__class__.__name__, self.tag, id(self)) 61 62 def makeelement(self, tag, attrib): 63 创建一个新节点 64 """Create a new element with the same type. 65 66 *tag* is a string containing the element name. 67 *attrib* is a dictionary containing the element attributes. 68 69 Do not call this method, use the SubElement factory function instead. 70 71 """ 72 return self.__class__(tag, attrib) 73 74 def copy(self): 75 """Return copy of current element. 76 77 This creates a shallow copy. Subelements will be shared with the 78 original tree. 79 80 """ 81 elem = self.makeelement(self.tag, self.attrib) 82 elem.text = self.text 83 elem.tail = self.tail 84 elem[:] = self 85 return elem 86 87 def __len__(self): 88 return len(self._children) 89 90 def __bool__(self): 91 warnings.warn( 92 "The behavior of this method will change in future versions. " 93 "Use specific 'len(elem)' or 'elem is not None' test instead.", 94 FutureWarning, stacklevel=2 95 ) 96 return len(self._children) != 0 # emulate old behaviour, for now 97 98 def __getitem__(self, index): 99 return self._children[index] 100 101 def __setitem__(self, index, element): 102 # if isinstance(index, slice): 103 # for elt in element: 104 # assert iselement(elt) 105 # else: 106 # assert iselement(element) 107 self._children[index] = element 108 109 def __delitem__(self, index): 110 del self._children[index] 111 112 def append(self, subelement): 113 为当前节点追加一个子节点 114 """Add *subelement* to the end of this element. 115 116 The new element will appear in document order after the last existing 117 subelement (or directly after the text, if it's the first subelement), 118 but before the end tag for this element. 119 120 """ 121 self._assert_is_element(subelement) 122 self._children.append(subelement) 123 124 def extend(self, elements): 125 为当前节点扩展 n 个子节点 126 """Append subelements from a sequence. 127 128 *elements* is a sequence with zero or more elements. 129 130 """ 131 for element in elements: 132 self._assert_is_element(element) 133 self._children.extend(elements) 134 135 def insert(self, index, subelement): 136 在当前节点的子节点中插入某个节点,即:为当前节点创建子节点,然后插入指定位置 137 """Insert *subelement* at position *index*.""" 138 self._assert_is_element(subelement) 139 self._children.insert(index, subelement) 140 141 def _assert_is_element(self, e): 142 # Need to refer to the actual Python implementation, not the 143 # shadowing C implementation. 144 if not isinstance(e, _Element_Py): 145 raise TypeError('expected an Element, not %s' % type(e).__name__) 146 147 def remove(self, subelement): 148 在当前节点在子节点中删除某个节点 149 """Remove matching subelement. 150 151 Unlike the find methods, this method compares elements based on 152 identity, NOT ON tag value or contents. To remove subelements by 153 other means, the easiest way is to use a list comprehension to 154 select what elements to keep, and then use slice assignment to update 155 the parent element. 156 157 ValueError is raised if a matching element could not be found. 158 159 """ 160 # assert iselement(element) 161 self._children.remove(subelement) 162 163 def getchildren(self): 164 获取所有的子节点(废弃) 165 """(Deprecated) Return all subelements. 166 167 Elements are returned in document order. 168 169 """ 170 warnings.warn( 171 "This method will be removed in future versions. " 172 "Use 'list(elem)' or iteration over elem instead.", 173 DeprecationWarning, stacklevel=2 174 ) 175 return self._children 176 177 def find(self, path, namespaces=None): 178 获取第一个寻找到的子节点 179 """Find first matching element by tag name or path. 180 181 *path* is a string having either an element tag or an XPath, 182 *namespaces* is an optional mapping from namespace prefix to full name. 183 184 Return the first matching element, or None if no element was found. 185 186 """ 187 return ElementPath.find(self, path, namespaces) 188 189 def findtext(self, path, default=None, namespaces=None): 190 获取第一个寻找到的子节点的内容 191 """Find text for first matching element by tag name or path. 192 193 *path* is a string having either an element tag or an XPath, 194 *default* is the value to return if the element was not found, 195 *namespaces* is an optional mapping from namespace prefix to full name. 196 197 Return text content of first matching element, or default value if 198 none was found. Note that if an element is found having no text 199 content, the empty string is returned. 200 201 """ 202 return ElementPath.findtext(self, path, default, namespaces) 203 204 def findall(self, path, namespaces=None): 205 获取所有的子节点 206 """Find all matching subelements by tag name or path. 207 208 *path* is a string having either an element tag or an XPath, 209 *namespaces* is an optional mapping from namespace prefix to full name. 210 211 Returns list containing all matching elements in document order. 212 213 """ 214 return ElementPath.findall(self, path, namespaces) 215 216 def iterfind(self, path, namespaces=None): 217 获取所有指定的节点,并创建一个迭代器(可以被for循环) 218 """Find all matching subelements by tag name or path. 219 220 *path* is a string having either an element tag or an XPath, 221 *namespaces* is an optional mapping from namespace prefix to full name. 222 223 Return an iterable yielding all matching elements in document order. 224 225 """ 226 return ElementPath.iterfind(self, path, namespaces) 227 228 def clear(self): 229 清空节点 230 """Reset element. 231 232 This function removes all subelements, clears all attributes, and sets 233 the text and tail attributes to None. 234 235 """ 236 self.attrib.clear() 237 self._children = [] 238 self.text = self.tail = None 239 240 def get(self, key, default=None): 241 获取当前节点的属性值 242 """Get element attribute. 243 244 Equivalent to attrib.get, but some implementations may handle this a 245 bit more efficiently. *key* is what attribute to look for, and 246 *default* is what to return if the attribute was not found. 247 248 Returns a string containing the attribute value, or the default if 249 attribute was not found. 250 251 """ 252 return self.attrib.get(key, default) 253 254 def set(self, key, value): 255 为当前节点设置属性值 256 """Set element attribute. 257 258 Equivalent to attrib[key] = value, but some implementations may handle 259 this a bit more efficiently. *key* is what attribute to set, and 260 *value* is the attribute value to set it to. 261 262 """ 263 self.attrib[key] = value 264 265 def keys(self): 266 获取当前节点的所有属性的 key 267 268 """Get list of attribute names. 269 270 Names are returned in an arbitrary order, just like an ordinary 271 Python dict. Equivalent to attrib.keys() 272 273 """ 274 return self.attrib.keys() 275 276 def items(self): 277 获取当前节点的所有属性值,每个属性都是一个键值对 278 """Get element attributes as a sequence. 279 280 The attributes are returned in arbitrary order. Equivalent to 281 attrib.items(). 282 283 Return a list of (name, value) tuples. 284 285 """ 286 return self.attrib.items() 287 288 def iter(self, tag=None): 289 在当前节点的子孙中根据节点名称寻找所有指定的节点,并返回一个迭代器(可以被for循环)。 290 """Create tree iterator. 291 292 The iterator loops over the element and all subelements in document 293 order, returning all elements with a matching tag. 294 295 If the tree structure is modified during iteration, new or removed 296 elements may or may not be included. To get a stable set, use the 297 list() function on the iterator, and loop over the resulting list. 298 299 *tag* is what tags to look for (default is to return all elements) 300 301 Return an iterator containing all the matching elements. 302 303 """ 304 if tag == "*": 305 tag = None 306 if tag is None or self.tag == tag: 307 yield self 308 for e in self._children: 309 yield from e.iter(tag) 310 311 # compatibility 312 def getiterator(self, tag=None): 313 # Change for a DeprecationWarning in 1.4 314 warnings.warn( 315 "This method will be removed in future versions. " 316 "Use 'elem.iter()' or 'list(elem.iter())' instead.", 317 PendingDeprecationWarning, stacklevel=2 318 ) 319 return list(self.iter(tag)) 320 321 def itertext(self): 322 在当前节点的子孙中根据节点名称寻找所有指定的节点的内容,并返回一个迭代器(可以被for循环)。 323 """Create text iterator. 324 325 The iterator loops over the element and all subelements in document 326 order, returning all inner text. 327 328 """ 329 tag = self.tag 330 if not isinstance(tag, str) and tag is not None: 331 return 332 if self.text: 333 yield self.text 334 for e in self: 335 yield from e.itertext() 336 if e.tail: 337 yield e.tail
由于 每个节点 都具有以上的方法,并且在上一步骤中解析时均得到了root(xml文件的根节点),so 可以利用以上方法进行操作xml文件。
a. 遍历XML文档的所有内容
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ############ 解析方式一 ############ 4 """ 5 # 打开文件,读取XML内容 6 str_xml = open('xo.xml', 'r').read() 7 8 # 将字符串解析成xml特殊对象,root代指xml文件的根节点 9 root = ET.XML(str_xml) 10 """ 11 ############ 解析方式二 ############ 12 13 # 直接解析xml文件 14 tree = ET.parse("xo.xml") 15 16 # 获取xml文件的根节点 17 root = tree.getroot() 18 19 20 ### 操作 21 22 # 顶层标签 23 print(root.tag) 24 25 26 # 遍历XML文档的第二层 27 for child in root: 28 # 第二层节点的标签名称和标签属性 29 print(child.tag, child.attrib) 30 # 遍历XML文档的第三层 31 for i in child: 32 # 第二层节点的标签名称和内容 33 print(i.tag,i.text)
b、遍历XML中指定的节点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ############ 解析方式一 ############ 4 """ 5 # 打开文件,读取XML内容 6 str_xml = open('xo.xml', 'r').read() 7 8 # 将字符串解析成xml特殊对象,root代指xml文件的根节点 9 root = ET.XML(str_xml) 10 """ 11 ############ 解析方式二 ############ 12 13 # 直接解析xml文件 14 tree = ET.parse("xo.xml") 15 16 # 获取xml文件的根节点 17 root = tree.getroot() 18 19 20 ### 操作 21 22 # 顶层标签 23 print(root.tag) 24 25 26 # 遍历XML中所有的year节点 27 for node in root.iter('year'): 28 # 节点的标签名称和内容 29 print(node.tag, node.text)
c、修改节点内容
由于修改的节点时,均是在内存中进行,其不会影响文件中的内容。所以,如果想要修改,则需要重新将内存中的内容写到文件。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ############ 解析方式一 ############ 4 5 # 打开文件,读取XML内容 6 str_xml = open('xo.xml', 'r').read() 7 8 # 将字符串解析成xml特殊对象,root代指xml文件的根节点 9 root = ET.XML(str_xml) 10 11 ############ 操作 ############ 12 13 # 顶层标签 14 print(root.tag) 15 16 # 循环所有的year节点 17 for node in root.iter('year'): 18 # 将year节点中的内容自增一 19 new_year = int(node.text) + 1 20 node.text = str(new_year) 21 22 # 设置属性 23 node.set('name', 'alex') 24 node.set('age', '18') 25 # 删除属性 26 del node.attrib['name'] 27 28 29 ############ 保存文件 ############ 30 tree = ET.ElementTree(root) 31 tree.write("newnew.xml", encoding='utf-8') 32 33 解析字符串方式,修改,保存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ############ 解析方式二 ############ 4 5 # 直接解析xml文件 6 tree = ET.parse("xo.xml") 7 8 # 获取xml文件的根节点 9 root = tree.getroot() 10 11 ############ 操作 ############ 12 13 # 顶层标签 14 print(root.tag) 15 16 # 循环所有的year节点 17 for node in root.iter('year'): 18 # 将year节点中的内容自增一 19 new_year = int(node.text) + 1 20 node.text = str(new_year) 21 22 # 设置属性 23 node.set('name', 'alex') 24 node.set('age', '18') 25 # 删除属性 26 del node.attrib['name'] 27 28 29 ############ 保存文件 ############ 30 tree.write("newnew.xml", encoding='utf-8') 31 32 解析文件方式,修改,保存
d、删除节点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ############ 解析字符串方式打开 ############ 4 5 # 打开文件,读取XML内容 6 str_xml = open('xo.xml', 'r').read() 7 8 # 将字符串解析成xml特殊对象,root代指xml文件的根节点 9 root = ET.XML(str_xml) 10 11 ############ 操作 ############ 12 13 # 顶层标签 14 print(root.tag) 15 16 # 遍历data下的所有country节点 17 for country in root.findall('country'): 18 # 获取每一个country节点下rank节点的内容 19 rank = int(country.find('rank').text) 20 21 if rank > 50: 22 # 删除指定country节点 23 root.remove(country) 24 25 ############ 保存文件 ############ 26 tree = ET.ElementTree(root) 27 tree.write("newnew.xml", encoding='utf-8') 28 29 解析字符串方式打开,删除,保存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ############ 解析文件方式 ############ 4 5 # 直接解析xml文件 6 tree = ET.parse("xo.xml") 7 8 # 获取xml文件的根节点 9 root = tree.getroot() 10 11 ############ 操作 ############ 12 13 # 顶层标签 14 print(root.tag) 15 16 # 遍历data下的所有country节点 17 for country in root.findall('country'): 18 # 获取每一个country节点下rank节点的内容 19 rank = int(country.find('rank').text) 20 21 if rank > 50: 22 # 删除指定country节点 23 root.remove(country) 24 25 ############ 保存文件 ############ 26 tree.write("newnew.xml", encoding='utf-8') 27 28 解析文件方式打开,删除,保存
3、创建XML文档
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 4 # 创建根节点 5 root = ET.Element("famliy") 6 7 8 # 创建节点大儿子 9 son1 = ET.Element('son', {'name': '儿1'}) 10 # 创建小儿子 11 son2 = ET.Element('son', {"name": '儿2'}) 12 13 # 在大儿子中创建两个孙子 14 grandson1 = ET.Element('grandson', {'name': '儿11'}) 15 grandson2 = ET.Element('grandson', {'name': '儿12'}) 16 son1.append(grandson1) 17 son1.append(grandson2) 18 19 20 # 把儿子添加到根节点中 21 root.append(son1) 22 root.append(son1) 23 24 tree = ET.ElementTree(root) 25 tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False) 26 27 创建方式(一)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 # 创建根节点 4 root = ET.Element("famliy") 5 6 7 # 创建大儿子 8 # son1 = ET.Element('son', {'name': '儿1'}) 9 son1 = root.makeelement('son', {'name': '儿1'}) 10 # 创建小儿子 11 # son2 = ET.Element('son', {"name": '儿2'}) 12 son2 = root.makeelement('son', {"name": '儿2'}) 13 14 # 在大儿子中创建两个孙子 15 # grandson1 = ET.Element('grandson', {'name': '儿11'}) 16 grandson1 = son1.makeelement('grandson', {'name': '儿11'}) 17 # grandson2 = ET.Element('grandson', {'name': '儿12'}) 18 grandson2 = son1.makeelement('grandson', {'name': '儿12'}) 19 20 son1.append(grandson1) 21 son1.append(grandson2) 22 23 24 # 把儿子添加到根节点中 25 root.append(son1) 26 root.append(son1) 27 28 tree = ET.ElementTree(root) 29 tree.write('oooo.xml',encoding='utf-8', short_empty_elements=False) 30 31 创建方式(二)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 4 # 创建根节点 5 root = ET.Element("famliy") 6 7 8 # 创建节点大儿子 9 son1 = ET.SubElement(root, "son", attrib={'name': '儿1'}) 10 # 创建小儿子 11 son2 = ET.SubElement(root, "son", attrib={"name": "儿2"}) 12 13 # 在大儿子中创建一个孙子 14 grandson1 = ET.SubElement(son1, "age", attrib={'name': '儿11'}) 15 grandson1.text = '孙子' 16 17 18 et = ET.ElementTree(root) #生成文档对象 19 et.write("test.xml", encoding="utf-8", xml_declaration=True, short_empty_elements=False) 20 21 创建方式(三)
由于原生保存的XML时默认无缩进,如果想要设置缩进的话, 需要修改保存方式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 from xml.dom import minidom 3 4 5 def prettify(elem): 6 """将节点转换成字符串,并添加缩进。 7 """ 8 rough_string = ET.tostring(elem, 'utf-8') 9 reparsed = minidom.parseString(rough_string) 10 return reparsed.toprettyxml(indent="\t") 11 12 # 创建根节点 13 root = ET.Element("famliy") 14 15 16 # 创建大儿子 17 # son1 = ET.Element('son', {'name': '儿1'}) 18 son1 = root.makeelement('son', {'name': '儿1'}) 19 # 创建小儿子 20 # son2 = ET.Element('son', {"name": '儿2'}) 21 son2 = root.makeelement('son', {"name": '儿2'}) 22 23 # 在大儿子中创建两个孙子 24 # grandson1 = ET.Element('grandson', {'name': '儿11'}) 25 grandson1 = son1.makeelement('grandson', {'name': '儿11'}) 26 # grandson2 = ET.Element('grandson', {'name': '儿12'}) 27 grandson2 = son1.makeelement('grandson', {'name': '儿12'}) 28 29 son1.append(grandson1) 30 son1.append(grandson2) 31 32 33 # 把儿子添加到根节点中 34 root.append(son1) 35 root.append(son1) 36 37 38 raw_str = prettify(root) 39 40 f = open("xxxoo.xml",'w',encoding='utf-8') 41 f.write(raw_str) 42 f.close()
4、命名空间
详细介绍:http://www.w3school.com.cn/xml/xml_namespaces.asp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from xml.etree import ElementTree as ET 2 3 ET.register_namespace('com',"http://www.company.com") #some name 4 5 # build a tree structure 6 root = ET.Element("{http://www.company.com}STUFF") 7 body = ET.SubElement(root, "{http://www.company.com}MORE_STUFF", attrib={"{http://www.company.com}hhh": "123"}) 8 body.text = "STUFF EVERYWHERE!" 9 10 # wrap it in an ElementTree instance, and save as XML 11 tree = ET.ElementTree(root) 12 13 tree.write("page.xml", 14 xml_declaration=True, 15 encoding='utf-8', 16 method="xml") 17 18 命名空间
5.re模块(* * * * *)
正则表达式使用反斜杠” \ “来代表特殊形式或用作转义字符,这里跟Python的语法冲突,因此,Python用” \\\\ “表示正则表达式中的” \ “,因为正则表达式中如果要匹配” \ “,需要用\来转义,变成” \\ “,而Python语法中又需要对字符串中每一个\进行转义,所以就变成了” \\\\ “。
上面的写法是不是觉得很麻烦,为了使正则表达式具有更好的可读性,Python特别设计了原始字符串(raw string),需要提醒你的是,在写文件路径的时候就不要使用raw string了,这里存在陷阱。raw string就是用'r'作为字符串的前缀,如 r”\n”:表示两个字符”\”和”n”,而不是换行符了。Python中写正则表达式时推荐使用这种形式。
正则表达式元字符说明
. 匹配除换行符以外的任意字符 ^ 匹配字符串的开始 $ 匹配字符串的结束 [] 用来匹配一个指定的字符类别 ? 对于前一个字符字符重复0次到1次 * 对于前一个字符重复0次到无穷次 {} 对于前一个字符重复m次 {m,n} 对前一个字符重复为m到n次 \d 匹配数字,相当于[0-9] \D 匹配任何非数字字符,相当于[^0-9] \s 匹配任意的空白符,相当于[ fv] \S 匹配任何非空白字符,相当于[^ fv] \w 匹配任何字母数字字符,相当于[a-zA-Z0-9_] \W 匹配任何非字母数字字符,相当于[^a-zA-Z0-9_] \b 匹配单词的开始或结束
模块函数说明即举例
1.re.compile 将正则表达式编译成pattern对象
compile(pattern, flags=0)
第一个参数:规则
第二个参数:标志位
2.re.match
只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None
match(pattern, string, flags=0)
第一个参数:规则
第二个参数:表示要匹配的字符串
第三个参数:标致位,用于控制正则表达式的匹配方式
3.re.search
匹配整个字符串,直到找到一个匹配
search(pattern, string, flags=0)
第一个参数:规则
第二个参数:表示要匹配的字符串
第三个参数:标致位,用于控制正则表达式的匹配方式
1 >>> import re 2 >>> pattern = re.compile(r'linuxeye') 3 >>> match = pattern.match('jb51.net') 4 >>> print match 5 <_sre.SRE_Match object at 0x7f4e96e61c60> 6 >>> print match.group() 7 linuxeye 8 >>> m = pattern.match('blog.jb51.net') #match匹配开头,没找到 9 >>> print m 10 None 11 >>> m = pattern.search('blog.jb51.net') #search匹配整个字符串,直到找到一个匹配 12 >>> print m 13 <_sre.SRE_Match object at 0x7f15abfc6b28> 14 >>> print m.group() 15 linuxeye
1 >>> m = re.match(r'linuxeye','jb51.net') #不用re.compile 2 >>> print m 3 <_sre.SRE_Match object at 0x7f4e96e61b90> 4 >>> print m.group() 5 linuxeye 6 >>> m = re.match(r'linuxeye','www.jb51.net') 7 >>> print m 8 None 9
4.re.split
用于来分割字符串
split(pattern, string, maxsplit=0)
第一个参数:规则
第二个参数:字符串
第三个参数:最大分割字符串,默认为0,表示每个匹配项都分割
实例:分割所有的字符串
1 >>> import re 2 >>> test_str = "1 2 3 4 5" 3 >>> re.split(r'\s+',test_str) 4 ['1', '2', '3', '4', '5'] 5 >>> re.split(r'\s+',test_str,2) #分割前2个 6 ['1', '2', '3 4 5'] 7 8 >>> test_str = "1 . 2. 3 .4 . 5" 9 >>> re.split(r'\.',test_str) 10 ['1 ', ' 2', ' 3 ', '4 ', ' 5'] 11 >>> re.split(r'\.',test_str,3) 12 ['1 ', ' 2', ' 3 ', '4 . 5']
5.re.findall
在目标字符串查找符合规则的字符串
findall(pattern, string, flags=0)
第一个参数:规则
第二个参数:目标字符串
但三个参数:后面还可以跟一个规则选择项
返回的结果是一个列表,建中存放的是符合规则的字符串,如果没有符合规则的字符串呗找到,就会返回一个空值
1 >>> import re 2 >>> test_mail = '<test01@gmail.com> <test02@gmail.org> test03@gmail.net' 3 >>> mail_re = re.compile(r'\w+@g....\.[a-z]{3}') 4 >>> re.findall(mail_re,test_mail) 5 ['test01@gmail.com', 'test02@gmail.org', 'test03@gmail.net']
6.re.sub
以正则表达式为基础的替换工作
sub(pattern, repl, string, count=0)
第一个参数:规则
第二个参数:替换后的字符串
第三个参数:字符串
第四个参数:替换个数。默认为0,表示每个匹配项都替换
1 >>> test = 'blog.jb51.net jb51.net' 2 >>> test_re = re.compile(r'\.') 3 >>> re.sub(test_re,'--',test) 4 'blog--linuxeye--com linuxeye--com' 5 >>> re.sub(test_re,'--',test,1) 6 'blog--jb51.net jb51.net'