15-常用模块
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了15-常用模块相关的知识,希望对你有一定的参考价值。
目录:
time,random,os,sys,shutil,json&pickle,shelve,xml,configparser,hashlib,suprocess,logging,re,
常用模块
常见模块的分类:
内置模块自定义模块第三方模块
random
随机数
random.random():
random.randint():
random.randrange():
random.choice():
random.sample():
random.uniform():
random.shuffle():
import random
print(random.random()) # 随机显示大于0且小于1之间的某个小数
print(random.randint(1, 3)) # 随机显示一个大于等于1且小于等于3之间的整数
print(random.randrange(1, 3)) # 随机显示一个大于等于1且小于3之间的整数
print(random.choice([1, ‘23‘, [4, 5]])) # 随机选择列表中的元素,1或者23或者[4,5]
print(random.sample([1, ‘23‘, [4, 5]], 2)) # 随机选择列表元素任意2个组合,拼成一个新的列表
print(random.uniform(1, 3)) # 随机选择大于1小于3的小数,如1.927109612082716
item = [1, 3, 5, 7, 9]
random.shuffle(item) # 打乱item的顺序,相当于"洗牌"
print(item)
运行结果#->
0.8411519662343422
2
1
1
[[4, 5], ‘23‘]
1.1835899789185504
[9, 7, 1, 3, 5]
应用示例:随机验证码
import random
def v_code(n=5):
res=‘‘
for i in range(n):
num=random.randint(0,9) # 随机选择数字
s=chr(random.randint(65,90)) # 随机选择字母,chr()转换数字为ASCII码对应的字母
add=random.choice([num,s])
res+=str(add) #res = res + str(add) 字符串拼接
return res
print(v_code(4))
运行结果:
91W2
time
在Python中,通常有这几种方式来表示时间:
1-时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行“type(time.time())”,返回的是float类型。
2-格式化的时间字符串(Format String)
3-结构化的时间(struct_time):struct_time元组共有9个元素共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
import time
#我们先以当前时间为准,快速认识三种形式的时间print(time.time()) # 时间戳:1487130156.419527,计算机用的时间
print(time.strftime("%Y-%m-%d %X")) #格式化的时间字符串:‘2017-02-15 11:40:53‘
print(time.localtime()) #本地时区的struct_time,结构化的时间
print(time.gmtime()) #UTC时区的struct_time,标准时间
运行结果:
1496747605.4952035
2017-06-06 19:13:25
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=6, tm_hour=19, tm_min=13, tm_sec=25, tm_wday=1, tm_yday=157, tm_isdst=0)
time.struct_time(tm_year=2017, tm_mon=6, tm_mday=6, tm_hour=11, tm_min=13, tm_sec=25, tm_wday=1, tm_yday=157, tm_isdst=0)
其中计算机认识的时间只能是‘时间戳‘格式,而程序员可处理的或者说人类能看懂的时间有: ‘格式化的时间字符串‘,‘结构化的时间‘ ,于是有了下图的转换关系:
转换示例:
print(time.localtime(time.time())) #将当前时间戳转换成结构化的时间
#->time.struct_time(tm_year=2017, tm_mon=6, tm_mday=6, tm_hour=21, tm_min=0, tm_sec=25, tm_wday=1, tm_yday=157, tm_isdst=0)
print(time.mktime(time.localtime())) #将当前的结构化时间转换为时间戳格式的时间
#->1496754025.0
print(time.strftime(‘%Y-%m-%d %X‘, time.localtime())) #将当前的结构化的时间转换为格式化的字符串时间
#->2017-06-06 21:00:25
print(time.strptime(‘2017-06-06 11:59:59‘, ‘%Y-%m-%d %X‘)) #将字符串时间转换为结构化的时间
#->time.struct_time(tm_year=2017, tm_mon=6, tm_mday=6, tm_hour=11, tm_min=59, tm_sec=59, tm_wday=1, tm_yday=157, tm_isdst=-1)
print(time.ctime(time.time())) #将当前时间戳转换成linux形式的时间格式
#->Tue Jun 6 21:07:21 2017
print(time.asctime(time.localtime())) #将当前结构化的时间转换为linux形式的时间格式
#->Tue Jun 6 21:07:21 2017
print(time.asctime()) #linux默认形式的时间格式
print(time.asctime())
print(time.strftime(‘%a %b %d %H:%M:%S %Y‘, time.localtime()))
运行结果#->
Tue Jun 6 21:04:54 2017
Tue Jun 06 21:04:54 2017
其他用法:
time.sleep(t): 线程推迟指定的时间运行,单位秒,就是睡几秒在往下执行
os
os模块是与操作系统交互的一个接口
os.sep:显示当前操作系统平台特定的路径分隔符,win为‘\\‘,linux为‘/‘
os.linesep:给出当前平台的行终止符。例如,Windows使用‘\r\n‘,Linux使用‘\n‘,而Mac使用‘\r‘
os.name:指示你正在使用的工作平台。比如对于Windows,它是‘nt‘,而对于Linux/Unix用户,它是‘posix‘。
os.pathsep:输出用于分割文件路径的字符串,win下为‘;‘,linux下为‘:‘
os.getcwd:得到当前工作目录,即当前python脚本工作的目录路径。
os.chdir(‘dirname‘):改变当前工作目录到dirname,相当于shell下的cd
os.curdir:返回当前目录(‘.‘)
os.pardir:获取当前目录的父目录字符串名(‘..‘)
os.mkdir(‘dirname‘):创建单级目录,相当于shell下mkdir dirname
os.makedirs(‘dirname1/dirname2‘):递归创建多层目录
os.removedirs(‘dirname‘):删除多级目录,若目录为空,则删除,并递归到上级目录,若也为空,也删除
os.listdir(‘dirname‘):列出指定目录下的所有文件和子目录,包括隐藏文件,生成列表形式
os.remove(‘filename‘):删除一个文件
os.rename(‘oldname‘,‘newname‘):重命名文件或目录
os.stat(‘path/filename‘):获得文件或目录属性信息
os.getenv()和os.putenv:分别用来读取和设置环境变量
os.chmod(‘filename‘):修改文件权限和时间戳
os.rmdir(‘dirname‘):删除目录
os.system(‘bash command‘):运行shell命令,直接显示
os.exit():终止当前进程
os.path.abspath(name):获得绝对路径
os.path.getsize(name):获取文件大小,如果name是目录返回0L
os.path.getatime(path):获取path指向的文件或目录的最后存取时间
os.path.getmtime(path):获取path指向的文件或目录的最后修改时间
os.path.split():返回一个路径的目录名和文件名
os.path.normpath(path):规范path字符串形式
os.path.split(name):分割文件名与目录(事实上,如果你完全使用目录,它也会将最后一个目录作为文件名而分离,同时它不会判断文件或目录是否存在)
os.path.splitext():分离文件名和扩展名
os.path.join(path,name):连接目录与文件名或目录
os.path.basename(path):返回path最后的文件名,如果path以/或\结尾,则返回空值
os.path.dirname(path):返回文件路径
os.path.isfile()和os.path.isdir()分别检验给出的路径是一个目录还是文件
os.path.existe():检验给出的路径是否真的存在
os.path.isdir(name):判断name是不是目录,不是目录就返回false
os.path.isfile(name):判断name这个文件是否存在,不存在返回false
os.path.exists(name):判断是否存在文件或目录name
os.path.isabs():判断是否为绝对路径
os.path.normcase(‘path‘):在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。
os.path.normcase(‘c:/windows\\system32\\‘)
#-> ‘c:\\windows\\system32\\‘
os.path.normpath(‘path‘):规范化路径,如‘..‘和‘/‘
os.path.normpath(‘c://windows\\System32\\../Temp/‘)
#-> ‘c:\\windows\\Temp‘
a=‘/Users/jieli/test1/\\\a1/\\\\aa.py/../..‘
print(os.path.normpath(a))
#-> /Users/jieli/test1
路径处理:
os路径处理
#方式一:推荐使用
import os
#具体应用
import os,sys
possible_topdir = os.path.normpath(os.path.join(
os.path.abspath(__file__),
os.pardir, #上一级
os.pardir,
os.pardir
))
sys.path.insert(0,possible_topdir)
#方式二:不推荐使用
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
目录操作
os.mkdir("file")
创建目录
复制文件:
shutil.copyfile("oldfile","newfile")
oldfile和newfile都只能是文件
shutil.copy("oldfile","newfile")
oldfile只能是文件夹,newfile可以是文件,也可以是目标目录
shutil.copytree("olddir","newdir")
复制文件夹.olddir和newdir都只能是目录,且newdir必须不存在
os.rename("oldname","newname")
重命名文件(目录).文件或目录都是使用这条命令
shutil.move("oldpos","newpos")
移动文件(目录)
os.rmdir("dir")
只能删除空目录
shutil.rmtree("dir")
空目录、有内容的目录都可以删
sys
常见模块:
sys.argv:在外部向程序内部传递参数
例如:
#文件:argv.py
#!/usr/bin/enc python
#
import sys
print(sys.argv) #显示文件所在绝对路径,为列表的第0个元素
print(sys.argv[1]) #以空格为分隔符,将后面的字符串生成一个列表,显示列表第一个元素
print(sys.argv[2]) #显示第二个元素
#->在命令行终端执行:
>python F:\oldboy_python\objects\day06\练习\tmp.py --host 192.168.1.1 --port 8080
[‘F:\\oldboy_python\\objects\\day06\\\xc1\xb7\xcf\xb0\\tmp.py‘, ‘--host‘, ‘192.168.1.1‘, ‘--port‘, ‘8080‘]
--host
192.168.1.1
sys.exit(n):退出程序,正常退出时exit(0)
执行到主程序末尾,解释器自动退出,但是如果需要中途退出程序,可以调用sys.exit函数,带有一个可选的整数参数返回给调用它的程序,表示你可以在主程序中捕获对sys.exit的调用。(0是正常退出,其他为异常)
sys.modules:sys.modules是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules将自动记录该模块。当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。它拥有字典所拥有的一切方法
sys.version:获取python解释程序的版本信息
sys.getdefaultencoding():获取系统当前字符编码,一般默认为ASCII
sys.setdefaultencoding():设置系统默认编码,如果在解释器中执行 print(dir(sys)),找不到此方法,可以先执行reload(sys)
sys.getfilesystemencoding():获取文件系统使用的编码格式,win返回mbcs,mac返回utf-8
sys.maxint:最大的Int值
sys.path:返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值,可将自定义的模块放在此路径下,通过import可导入
sys.platform:返回操作系统平台名称
sys.stdin:标准输入
sys.stdout:标准输出
sys.stderr:标准错误输出
stdin , stdout , 以及stderr 变量包含与标准I/O 流对应的流对象. 如果需要更好地控制输出,而print 不能满足你的要求, 它们就是你所需要的. 你也可以替换它们, 这时候你就可以重定向输出和输入到其它设备( device ), 或者以非标准的方式处理它们。
应用实例:
打印#号进度条
import sys, time
for i in range(100):
sys.stdout.write(‘%s\r‘, %(‘#‘*i))
sys.stdout.flush()
time.sleep(0.1)
高级的文件、文件夹、压缩包处理模块
shutil.copyfileobj(‘old_file‘, ‘new_file‘):将文件内容拷贝到另一个文件中
import shutil
shutil.copyfileobj(open(‘old_file‘, ‘r‘), open(‘new_file‘, ‘w‘))
shutil.copyfile(src, dst):拷贝文件,dst文件无需存在,会自动创建,存在则覆盖
shutil.copymode(src, dst): 仅拷贝权限,内容、组、所有者都不变,dst文件必须事先存在
shutil.copystat(src, dst):仅拷贝文件状态信息,包括mode bits, atime, mtime, flags,dst文件需事先存在
shutil.copy(src, dst):拷贝文件和权限
shutil.copy2(src, dst):拷贝文件和状态信息
shutil.ignore_patterns(‘*patterns‘):
shutil.copytree(src, dst, symlinks=False|True, ignore=None):递归拷贝目录
--symlinks=False|True:为False表示将软链接文件拷贝成硬链接,就是说对软链接来说,创建新的文件,为True表示拷贝软链接
import shutil
shutil.copytree(‘folder1‘, ‘folder2‘, ignore=shutil.ignore_patterns(‘*.pyc‘, ‘tmp*‘))
# 递归拷贝folder1中的所有目录和文件,排除‘*.pyc‘, ‘tmp*‘文件,folder2事前不能存在,且对folder2的父目录有可写权限
shutil.rmtree(‘path/to/file‘, [ignore_errors]):递归删除文件
shutil.move(src, dst): 递归移动文件,类似mv,重命名
shutil.make_archive(‘base_name‘, ‘format‘, root_dir=r‘path/to/file‘, ...):归档压缩
创建压缩包并返回文件路径,例如:zip、tar
创建压缩包并返回文件路径,例如:zip、tar
--base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
-->如 data_bak=>保存至当前路径
-->如:/tmp/data_bak =>保存至/tmp/
--format:压缩包种类,“zip”, “tar”, “bztar”,“gztar”
--root_dir:要压缩的文件夹路径(默认当前目录)
--owner:用户,默认当前用户
--group:组,默认当前组
--logger:用于记录日志,通常是logging.Logger对象
#将 /data 下的文件打包放置当前程序目录
import shutil
ret = shutil.make_archive("data_bak", ‘gztar‘, root_dir=‘/data‘)
#将 /data下的文件打包放置 /tmp/目录
import shutil
ret = shutil.make_archive("/tmp/data_bak", ‘gztar‘, root_dir=‘/data‘)
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
import zipfile
# 压缩
z = zipfile.ZipFile(‘laxi.zip‘, ‘w‘)
z.write(‘a.log‘)
z.write(‘data.data‘)
z.close()
# 解压
z = zipfile.ZipFile(‘laxi.zip‘, ‘r‘)
z.extractall(path=‘.‘)
z.close()
压缩和解压示例:
import tarfile
# 压缩
>>> t=tarfile.open(‘/tmp/egon.tar‘,‘w‘)
>>> t.add(‘/test1/a.py‘,arcname=‘a.bak‘)
>>> t.add(‘/test1/b.py‘,arcname=‘b.bak‘)
>>> t.close()
# 解压
>>> t=tarfile.open(‘/tmp/egon.tar‘,‘r‘)
>>> t.extractall(‘/egon‘)
>>> t.close()
json&pickle
什么是序列化?
--我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思,
为什么要序列化?
1:持久保存状态
需知一个软件/程序的执行就在处理一系列状态的变化,在编程语言中,‘状态‘会以各种各样有结构的数据类型(也可简单的理解为变量)的形式被保存在内存中。
内存是无法永久保存数据的,当程序运行了一段时间,我们断电或者重启程序,内存中关于这个程序的之前一段时间的数据(有结构)都被清空了。
在断电或重启程序之前将程序当前内存中所有的数据都保存下来(保存到文件中),以便于下次程序执行能够从文件中载入之前的数据,然后继续执行,这就是序列化。
具体的来说,你玩使命召唤闯到了第13关,你保存游戏状态,关机走人,下次再玩,还能从上次的位置开始继续闯关。
或如,虚拟机状态的挂起等。
2:跨平台数据交互
序列化之后,不仅可以把序列化后的内容写入磁盘,还可以通过网络传输到别的机器上,如果收发的双方约定好实用一种序列化的格式,那么便打破了平台/语言差异化带来的限制,实现了跨平台数据交互。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
json
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
JSON还定死了字符集必须是UTF-8,表示多语言就没有问题了。为了统一解析,JSON的字符串规定必须用双引号"",Object的键也必须用双引号""
JSON表示的对象就是标准的javascript语言的对象,JSON和Python内置的数据类型对应如下:
import json
json.dumps():编码,把一个python对象编码转换成json字符串
json.loads():解码,把json格式字符串解码转换成python对象
注意:在json的数据格式中必须是双引号开头的
下面通过示例来说明使用json的好处:
使用eval(),不能转换python不支持的数据类型格式,如null,true,false等
import json
x="[null,true,false,1]"
print(eval(x))
print(json.loads(x))
运行结果:
print(eval(x)) #报错,无法解析null类型,而json就可以
File "<string>", line 1, in <module>
NameError: name ‘null‘ is not defined
使用json()
import json
x="[null,true,false,1]"
print(json.loads(x))
运行结果:
[None, True, False, 1]
使用json.dumps()进行序列化
过程:dic--> res=json.dumps(dic)-->f.write(res)
import json
dic = {
‘name‘: ‘alex‘,
‘age‘: 9000,
‘height‘: ‘180cm‘
}
res = json.dumps(dic) #编码,把一个python对象编码转换成json字符串
print(res,type(res))
with open(‘a.json‘, ‘w‘) as f:
f.write(res)
运行结果:
{"name": "alex", "age": 9000, "height": "180cm"} <class ‘str‘>
写入文件a.json,内容为{"name": "alex", "age": 9000, "height": "180cm"}
使用json.loads()反序列化
过程:f.read()--truncate>res=json.loads(res)-->dic=res
with open(‘a.json‘, ‘r‘) as f:
dic = json.loads(f.read())
print(dic, type(dic))
运行结果:
{‘age‘: 9000, ‘height‘: ‘180cm‘, ‘name‘: ‘alex‘} <class ‘dict‘>
json.dumps()的参数:
sort_keys:对dict对象进行排序
separators:压缩,移除多余空白
indent n :缩进n个空格
skipkeys: 默认False,dumps方法存储dict对象时,key必须是str类型,如果出现了其他类型的话,那么会产生TypeError异常,如果开启该参数,设为True的话,则会比较优雅的过度。
json的简单综合示例:
import json
json.dump(dic, open(‘b.json‘,‘w‘)) #序列化
res = json.load(open(‘b.json‘, ‘r‘)) #反序列化
print(res, type(res))
#->序列化结果:
生成新的文件b.json,内容为:{"name": "alex", "age": 9000, "height": "180cm"}
#->反序列化结果:
{‘name‘: ‘alex‘, ‘age‘: 9000, ‘height‘: ‘180cm‘} <class ‘dict‘>
注意:dumps对应loads;dump对应load
pickle
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
pickle将内存中的结构化的数据转换成bytes类型,然后用于存储或传输
例如:
import pickle
dic = {
‘name‘: ‘alex‘,
‘age‘: 9000,
‘height‘: ‘180cm‘
}
print(pickle.dumps(dic))
#->运行结果:
b‘\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x04\x00\x00\x00alexq\x02X\x03\x00\x00\x00ageq\x03M(#X\x06\x00\x00\x00heightq\x04X\x05\x00\x00\x00180cmq\x05u.‘
由于pickle转换成bytes格式,所以如果写入文件,需要以wb方式,读取文件以rb方式
pickle.dumps()与pickle.loads()
import pickle
dic = {
‘name‘: ‘alex‘,
‘age‘: 9000,
‘height‘: ‘180cm‘
}
with open(‘a.pickle‘, ‘wb‘) as f:
f.write(pickle.dumps(dic))
with open(‘a.pickle‘, ‘rb‘) as f:
a = pickle.loads(f.read())
print(a, type(a))
pickle.dump()与pickle.load()
import pickle
pickle.dump(dic, open(‘b.pickle‘, ‘wb‘))
res = pickle.load(open(‘b.pickle‘, ‘rb‘))
print(res, type(res))
pickle可序列化函数,json不可以
import json,pickle
def func():
print(‘from func...‘)
json.dumps(func)
#->
运行结果:#TypeError: <function func at 0x0000000000B7E1E0> is not JSON serializable
f = pickle.dumps(func)
print(f)
pickle.dump(func, open(‘c.pkl‘, ‘wb‘))
res = pickle.load(open(‘c.pkl‘, ‘rb‘))
print(res)
res()
#->运行结果:
b‘\x80\x03c__main__\nfunc\nq\x00.‘
<function func at 0x0000000000D9E1E0>
from func...
注意:序列化的函数如果不存在,则无法序列化,因为序列化的是一段内存地址,在内存中找不到其对应的名字
shelve
shelve模块比pickle模块简单,只有一个open函数,返回类似字典的对象,可读可写;
key必须为字符串,而值可以是python所支持的数据类型
import shelve
f=shelve.open(r‘sheve.txt‘)
f[‘stu1_info‘]={‘name‘:‘egon‘,‘age‘:18,‘hobby‘:[‘piao‘,‘smoking‘,‘drinking‘]}
f[‘stu2_info‘]={‘name‘:‘gangdan‘,‘age‘:53}
f[‘school_info‘]={‘website‘:‘http://www.pypy.org‘,‘city‘:‘beijing‘}
print(f[‘stu1_info‘][‘hobby‘])
f.close()
#->
会生成3个文件:sheve.txt.bak,sheve.txt.dat,sheve.txt.dir
[‘piao‘, ‘smoking‘, ‘drinking‘]
logging
import logging
import sys
一:filename=‘access.log‘:如果不指定filename,则默认打印到终端,指定filename,则会将日志信息写入此文件
二:指定日志级别:
指定方式:
1:level=10
2:level=logging.ERROR
日志级别种类:
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
三:指定日志级别为ERROR,则只有ERROR及其以上级别的日志会被打印
logging.basicConfig(
format=‘%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s‘,
datefmt=‘%Y-%m-%d %H:%M:%S %p‘,
level=10)
logging.debug(‘debug‘)
logging.info(‘info‘)
logging.warning(‘warning‘)
logging.error(‘error‘)
logging.critical(‘critical‘)
logging.log(10,‘log‘)
#如果level=40,则只有logging.critical和loggin.error的日志会被打印
#->
2017-06-07 22:57:16 PM - root - DEBUG -tmp: debug
2017-06-07 22:57:16 PM - root - INFO -tmp: info
2017-06-07 22:57:16 PM - root - WARNING -tmp: warning
2017-06-07 22:57:16 PM - root - ERROR -tmp: error
2017-06-07 22:57:16 PM - root - CRITICAL -tmp: critical
可在logging.basicConfig()函数中通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略
详细请参考:
http://blog.csdn.net/zyz511919766/article/details/25136485/
-------转载:http://python.jobbole.com/86887/-----------
基本用法
# 获取logger实例,如果参数为空则返回root logger
logger = logging.getLogger("AppName")
# 指定logger输出格式
formatter = logging.Formatter(‘%(asctime)s %(levelname)-8s: %(message)s‘)
# 文件日志
file_handler = logging.FileHandler("test.log")
file_handler.setFormatter(formatter) # 可以通过setFormatter指定输出格式
# 控制台日志
console_handler = logging.StreamHandler(sys.stdout)
console_handler.formatter = formatter # 也可以直接给formatter赋值
# 为logger添加的日志处理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 指定日志的最低输出级别,默认为WARN级别
logger.setLevel(logging.INFO)
# 输出不同级别的log
logger.debug(‘this is debug info‘)
logger.info(‘this is information‘)
logger.warn(‘this is warning message‘)
logger.error(‘this is error message‘)
logger.fatal(‘this is fatal message, it is same as logger.critical‘)
logger.critical(‘this is critical message‘)
# 2016-10-08 21:59:19,493 INFO : this is information
# 2016-10-08 21:59:19,493 WARNING : this is warning message
# 2016-10-08 21:59:19,493 ERROR : this is error message
# 2016-10-08 21:59:19,493 CRITICAL: this is fatal message, it is same as logger.critical
# 2016-10-08 21:59:19,493 CRITICAL: this is critical message
# 移除一些日志处理器
logger.removeHandler(file_handler)
格式化输出日志
# 格式化输出
service_name = "Booking"
logger.error(‘%s service is down!‘ % service_name) # 使用python自带的字符串格式化,不推荐
logger.error(‘%s service is down!‘, service_name) # 使用logger的格式化,推荐
logger.error(‘%s service is %s!‘, service_name, ‘down‘) # 多参数格式化
logger.error(‘{} service is {}‘.format(service_name, ‘down‘)) # 使用format函数,推荐
# 2016-10-08 21:59:19,493 ERROR : Booking service is down!
记录异常信息
当你使用logging模块记录异常信息时,不需要传入该异常对象,只要你直接调用logger.error() 或者 logger.exception()就可以将当前异常记录下来。
try:
1 / 0
except:
# 等同于error级别,但是会额外记录当前抛出的异常堆栈信息
logger.exception(‘this is an exception message‘)
# 2016-10-08 21:59:19,493 ERROR : this is an exception message
# Traceback (most recent call last):
# File "D:/Git/py_labs/demo/use_logging.py", line 45, in
# 1 / 0
# ZeroDivisionError: integer division or modulo by zero
Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:
logger提供了应用程序可以直接使用的接口;
handler将(logger创建的)日志记录发送到合适的目的输出;
filter提供了细度设备来决定输出哪条日志记录;
formatter决定日志记录的最终输出格式。
logger
每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:
LOG=logging.getLogger(”chat.gui”)
而核心模块可以这样:
LOG=logging.getLogger(”chat.kernel”)
Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高
Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filter
Logger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handler
Logger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别
Formatter日志格式
Formatter对象定义了log信息的结构和内容,构造时需要带两个参数:
一个是格式化的模板fmt,默认会包含最基本的level和 message信息
一个是格式化的时间样式datefmt,默认为 2003-07-08 16:49:45,896 (%Y-%m-%d %H:%M:%S)
fmt中允许使用的变量可以参考下表:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名|
%(funcName)s 调用日志输出函数的函数名|
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮点数表示|
%(relativeCreated)d 输出日志信息时的,自Logger创建以来的毫秒数|
%(asctime)s 字符串形式的当前时间。默认格式是“2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息
SetLevel 日志级别
Logging有如下级别: DEBUG,INFO,WARNING,ERROR,CRITICAL
默认级别是WARNING,logging模块只会输出指定level以上的log。这样的好处, 就是在项目开发时debug用的log,在产品release阶段不用一一注释,只需要调整logger的级别就可以了,很方便。
handler
handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handler
Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象
每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:
1) logging.StreamHandler
使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:
StreamHandler([strm])
其中strm参数是一个文件对象。默认是sys.stderr
2) logging.FileHandler
和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:
FileHandler(filename[,mode])
filename是文件名,必须指定一个文件名。
mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a‘,即添加到文件末尾。
3) logging.handlers.RotatingFileHandler
这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:
RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])
其中filename和mode两个参数和FileHandler一样。
maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。
backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。
4) logging.handlers.TimedRotatingFileHandler
这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时代表星期一)
midnight 每天凌晨
通过文件配置logging
如果你希望通过配置文件来管理logging,可以参考这个官方文档。在log4net或者log4j中这是很常见的方式。
# logging.conf
[loggers]
keys=root
[logger_root]
level=DEBUG
handlers=consoleHandler
#,timedRotateFileHandler,errorTimedRotateFileHandler
#################################################
[handlers]
keys=consoleHandler,timedRotateFileHandler,errorTimedRotateFileHandler
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)
[handler_timedRotateFileHandler]
class=handlers.TimedRotatingFileHandler
level=DEBUG
formatter=simpleFormatter
args=(‘debug.log‘, ‘H‘)
[handler_errorTimedRotateFileHandler]
class=handlers.TimedRotatingFileHandler
level=WARN
formatter=simpleFormatter
args=(‘error.log‘, ‘H‘)
#################################################
[formatters]
keys=simpleFormatter, multiLineFormatter
[formatter_simpleFormatter]
format= %(levelname)s %(threadName)s %(asctime)s: %(message)s
datefmt=%H:%M:%S
[formatter_multiLineFormatter]
format= ------------------------- %(levelname)s -------------------------
Time: %(asctime)s
Thread: %(threadName)s
File: %(filename)s(line %(lineno)d)
Message:
%(message)s
datefmt=%Y-%m-%d %H:%M:%S
假设以上的配置文件放在和模块相同的目录,代码中的调用如下:
import os
filepath = os.path.join(os.path.dirname(__file__), ‘logging.conf‘)
logging.config.fileConfig(filepath)
return logging.getLogger()
日志重复输出的坑
你有可能会看到你打的日志会重复显示多次,可能的原因有很多,但总结下来无非就一个,日志中使用了重复的handler。
第一坑
import logging
logging.basicConfig(level=logging.DEBUG)
fmt = ‘%(levelname)s:%(message)s‘
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(fmt))
logging.getLogger().addHandler(console_handler)
logging.info(‘hello!‘)
# INFO:root:hello!
# INFO:hello!
第二坑
import logging
def get_logger():
fmt = ‘%(levelname)s:%(message)s‘
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(fmt))
logger = logging.getLogger(‘App‘)
logger.setLevel(logging.INFO)
logger.addHandler(console_handler)
return logger
def call_me():
logger = get_logger()
logger.info(‘hi‘)
call_me()
call_me()
# INFO:hi
# INFO:hi
# INFO:hi
第三坑
import logging
def get_logger():
fmt = ‘%(levelname)s: %(message)s‘
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(fmt))
logger = logging.getLogger(‘App‘)
logger.setLevel(logging.INFO)
logger.addHandler(console_handler)
return logger
def foo():
logging.basicConfig(format=‘[%(name)s]: %(message)s‘)
logging.warn(‘some module use root logger‘)
def main():
logger = get_logger()
logger.info(‘App start.‘)
foo()
logger.info(‘App shutdown.‘)
main()
# INFO: App start.
# [root]: some module use root logger
# INFO: App shutdown.
# [App]: App shutdown.
如果你真的想禁用root logger,有两个不是办法的办法:
logging.getLogger().handlers = [] # 删除所有的handler
logging.getLogger().setLevel(logging.CRITICAL) # 将它的级别设置到最高
subprocess
subprocess的目的就是启动一个新的进程并且与之通信
subprocess模块中只定义了一个类: Popen
可以使用Popen来创建进程,并与进程进行复杂的交互
它的构造函数如下:
subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)
类Popen的方法:
Popen.poll():用于检查子进程是否已经结束。设置并返回returncode属性。
Popen.wait():等待子进程结束。设置并返回returncode属性。
Popen.send_signal(signal):向子进程发送信号。
Popen.terminate():停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill():杀死子进程。
Popen.pid:获取子进程的进程ID。
Popen.returncode:获取进程的返回值。如果进程还没有结束,返回None。
Popen.stdin,Popen.stdout ,Popen.stderr
官方说明:
stdin, stdout and stderr specify the executed programs’ standard input, standard output and standard error file handles, respectively. Valid values are PIPE, an existing file descriptor (a positive integer), an existing file object, and None.
Popen.communicate(input=None):与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
常用参数:
参数args可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。我们也可以显式的使用executeable参数来指定可执行文件的路径。
参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。
如果参数shell设为true,程序将通过当前shell来执行,比如在python解释器中执行,就会显示到解释器中
参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
subprocess.PIPE
管道,在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。
subprocess.STDOUT
创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。
----------------------------
简单用法示例:
p = subprocess.Popen(‘dir‘, shell=True)
#->
列出的就是文件当前目录内容
stdout
如果想得到进程的输出,管道是个很方便的方法:
正确的命令:
res=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(res)
print(res.stdout.read())
print(res.stdout.read().decode(‘gbk‘))
- #->
<subprocess.Popen object at 0x000000000067D438> #res
b‘ \xc7\xfd\xb6\xaf\xc6\xf7 F \xd....
#res.stdout.read()- b"‘dir45‘ \xb2\xbb\xca\xc7\xc4\xda\xb2.... #res.stderr.read()
驱动器 F 中的卷是 Bak
#res.stdout.read().decode(‘gbk‘),此处的gbk编码格式为当前系统编码格式卷的序列号是 AC2A-165F
F:\oldboy_python\objects\day06\练习 的目录
...
错误的命令:
res=subprocess.Popen("dir456", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(res)
print(res.stderr.read().decode(‘gbk‘))
#->
<subprocess.Popen object at 0x0000000000A1AA90>
‘dir456‘ 不是内部或外部命令,也不是可运行的程序
或批处理文件。
stdin
模拟一个管道示例:
#模拟 dir |grep txt$
res1=subprocess.Popen(r‘dir E:\wupeiqi\s17\day06‘,shell=True,stdout=subprocess.PIPE)
res=subprocess.Popen(r‘findstr txt*‘,shell=True,
stdin=res1.stdout,stderr=subprocess.PIPE,stdout=subprocess.PIPE)
注意:管道的内容只能取一次,再取则为空。
configparser
常见的配置文件格式:
#config.conf
# 注释1
; 注释2
[section1]
k1 = v1
k2:v2
user=egon
age=18
is_admin=true
salary=31
[section2]
k1 = v1
读取:
import configparser
config=configparser.ConfigParser()
config.read(‘a.cfg‘)
#查看所有的标题
res=config.sections() #[‘section1‘, ‘section2‘]
print(res)
#查看标题section1下所有key=value的key
options=config.options(‘section1‘)
print(options) #[‘k1‘, ‘k2‘, ‘user‘, ‘age‘, ‘is_admin‘, ‘salary‘]
#查看标题section1下所有key=value的(key,value)格式
item_list=config.items(‘section1‘)
print(item_list) #[(‘k1‘, ‘v1‘), (‘k2‘, ‘v2‘), (‘user‘, ‘egon‘), (‘age‘, ‘18‘), (‘is_admin‘, ‘true‘), (‘salary‘, ‘31‘)]
#查看标题section1下user的值=>字符串格式
val=config.get(‘section1‘,‘user‘)
print(val) #egon
#查看标题section1下age的值=>整数格式
val1=config.getint(‘section1‘,‘age‘)
print(val1) #18
#查看标题section1下is_admin的值=>布尔值格式
val2=config.getboolean(‘section1‘,‘is_admin‘)
print(val2) #True
#查看标题section1下salary的值=>浮点型格式
val3=config.getfloat(‘section1‘,‘salary‘)
print(val3) #31.0
改写:
import configparser
config=configparser.ConfigParser()
config.read(‘a.cfg‘)
#删除整个标题section2
config.remove_section(‘section2‘)
#删除标题section1下的某个k1和k2
config.remove_option(‘section1‘,‘k1‘)
config.remove_option(‘section1‘,‘k2‘)
#判断是否存在某个标题
print(config.has_section(‘section1‘))
#判断标题section1下是否有user
print(config.has_option(‘section1‘,‘‘))
#添加一个标题
config.add_section(‘egon‘)
#在标题egon下添加name=egon,age=18的配置
config.set(‘egon‘,‘name‘,‘egon‘)
config.set(‘egon‘,‘age‘,18) #报错,必须是字符串
#最后将修改的内容写入文件,完成最终的修改
config.write(open(‘a.cfg‘,‘w‘))
#test.ini
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
获取所有节点:
import configparser
config=configparser.ConfigParser()
config.read(‘test.ini‘,encoding=‘utf-8‘)
res=config.sections()
print(res)
‘‘‘
打印结果:
[‘bitbucket.org‘, ‘topsecret.server.com‘]
‘‘‘
获取指定节点下所有的键值对
import configparser
config=configparser.ConfigParser()
config.read(‘test.ini‘,encoding=‘utf-8‘)
res=config.items(‘bitbucket.org‘)
print(res)
‘‘‘
打印结果:(包含DEFAULT以及bitbucket.org这俩标题下所有的items)
[(‘serveraliveinterval‘, ‘45‘), (‘compression‘, ‘yes‘), (‘compressionlevel‘, ‘9‘), (‘forwardx11‘, ‘yes‘), (‘user‘, ‘hg‘)]
‘‘‘
获取指定节点下所有的键key
import configparser
config=configparser.ConfigParser()
config.read(‘test.ini‘,encoding=‘utf-8‘)
res=config.options(‘bitbucket.org‘)
print(res)
‘‘‘
打印结果:(包含DEFAULT以及bitbucket.org这俩标题下所有的键)
[‘user‘, ‘serveraliveinterval‘, ‘compression‘, ‘compressionlevel‘, ‘forwardx11‘]‘‘‘
获取指定节点下指定key的值
import configparser
config=configparser.ConfigParser()
config.read(‘test.ini‘,encoding=‘utf-8‘)
res1=config.get(‘bitbucket.org‘,‘user‘)
res2=config.getint(‘topsecret.server.com‘,‘port‘)
res3=config.getfloat(‘topsecret.server.com‘,‘port‘)
res4=config.getboolean(‘topsecret.server.com‘,‘ForwardX11‘)
print(res1)
print(res2)
print(res3)
print(res4)
‘‘‘
打印结果:
hg
50022.0
False
‘‘‘
检查删除添加节点:
import configparser
config=configparser.ConfigParser()
config.read(‘test.ini‘,encoding=‘utf-8‘)
#检查
has_sec=config.has_section(‘bitbucket.org‘)
print(has_sec) #打印True
#添加节点
config.add_section(‘egon‘) #已经存在则报错
config[‘egon‘][‘username‘]=‘gangdan‘
config[‘egon‘][‘age‘]=‘18‘
config.write(open(‘test.ini‘,‘w‘))
#删除节点
config.remove_section(‘egon‘)
config.write(open(‘test.ini‘,‘w‘))
检查删除设置指定组内的键值对
import configparser
config=configparser.ConfigParser()
config.read(‘test.ini‘,encoding=‘utf-8‘)
#检查
has_sec=config.has_option(‘bitbucket.org‘,‘user‘) #bitbucket.org下有一个键user
print(has_sec) #打印True
#删除
config.remove_option(‘DEFAULT‘,‘forwardx11‘)
config.write(open(‘test.ini‘,‘w‘))
#设置
config.set(‘bitbucket.org‘,‘user‘,‘gangdang‘)
config.write(open(‘test.ini‘,‘w‘))
基于上述方法添加一个ini模版文档
import configparser
config = configparser.ConfigParser()
config["DEFAULT"] = {‘ServerAliveInterval‘: ‘45‘,
‘Compression‘: ‘yes‘,
‘CompressionLevel‘: ‘9‘}
config[‘bitbucket.org‘] = {}
config[‘bitbucket.org‘][‘User‘] = ‘hg‘
config[‘topsecret.server.com‘] = {}
topsecret = config[‘topsecret.server.com‘]
topsecret[‘Host Port‘] = ‘50022‘ # mutates the parser
topsecret[‘ForwardX11‘] = ‘no‘ # same here
config[‘DEFAULT‘][‘ForwardX11‘] = ‘yes‘
with open(‘example.ini‘, ‘w‘) as configfile:
config.write(configfile)
xml
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>节点来区别数据结构的:
a.xml文件
<?xml version="1.0"?>
<data> #根节点
<country name="Liechtenstein"> #子节点
<rank updated="yes">2</rank>
<year>2008</year>
<gdppc>141100</gdppc>
<neighbor name="Austria" direction="E"/>
<neighbor name="Switzerland" direction="W"/>
</country>
<country name="Singapore">
#子节点<rank updated="yes">5</rank>
<year>2011</year>
<gdppc>59900</gdppc>
<neighbor name="Malaysia" direction="N"/>
</country>
<country name="Panama">
#子节点<rank updated="yes">69</rank>
<year>2011</year>
<gdppc>13600</gdppc>
<neighbor name="Costa Rica" direction="W"/>
<neighbor name="Colombia" direction="E"/>
</country>
</data>
xml数据
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
print(root.iter(‘year‘)) #全文搜索
print(root.find(‘country‘)) #在root的子节点找,只找一个
print(root.findall(‘country‘)) #在root的子节点找,找所有
xml文档操作示例:
import xml.etree.ElementTree as ET
tree = ET.parse("xmltest.xml")
root = tree.getroot()
print(root.tag)
#遍历xml文档
for child in root:
print(‘========>‘,child.tag,child.attrib,child.attrib[‘name‘])
for i in child:
print(i.tag,i.attrib,i.text)
#只遍历year 节点
for node in root.iter(‘year‘):
print(node.tag,node.text)
#---------------------------------------
import xml.etree.ElementTree as ET
tree = ET.parse("xmltest.xml")
root = tree.getroot()
#修改
for node in root.iter(‘year‘):
new_year=int(node.text)+1
node.text=str(new_year)
node.set(‘updated‘,‘yes‘)
node.set(‘version‘,‘1.0‘)
tree.write(‘test.xml‘)
#删除node
for country in root.findall(‘country‘):
rank = int(country.find(‘rank‘).text)
if rank > 50:
root.remove(country)
tree.write(‘output.xml‘)
#在country内添加(append)节点year2
import xml.etree.ElementTree as ET
tree = ET.parse("a.xml")
root=tree.getroot()
for country in root.findall(‘country‘):
for year in country.findall(‘year‘):
if int(year.text) > 2000:
year2=ET.Element(‘year2‘)
year2.text=‘新年‘
year2.attrib={‘update‘:‘yes‘}
country.append(year2) #往country节点下添加子节点
tree.write(‘a.xml.swap‘)
创建一个xml文档:
import xml.etree.ElementTree as ET
new_xml = ET.Element("namelist")
name = ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
age = ET.SubElement(name,"age",attrib={"checked":"no"})
sex = ET.SubElement(name,"sex")
sex.text = ‘33‘
name2 = ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
age = ET.SubElement(name2,"age")
age.text = ‘19‘
et = ET.ElementTree(new_xml) #生成文档对象
et.write("test.xml", encoding="utf-8",xml_declaration=True)
ET.dump(new_xml) #打印生成的格式
hashlib
hash:一种算法 ,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
三个特点:
1.内容相同则hash运算结果相同,内容稍微改变则hash值则变
2.不可逆推
3.相同算法:无论校验多长的数据,得到的哈希值长度固定。
import hashlib
m=hashlib.md5()
m.update(‘hello‘.encode(‘utf-8‘))
print(m.hexdigest())
#->运行结果:
5d41402abc4b2a76b9719d911017c592
update多次,与update一次,得到的结果一样,但update多次为校验大文件提供了可能
对文件进行校验:
a.txt文件内容:12345
import hashlib
m = hashlib.md5()
with open(r‘F:\oldboy_python\objects\day06\练习\a.txt‘, ‘rb‘) as f:
for line in f:
m.update(line)
md5_num = m.hexdigest()
print(md5_num)
#->
827ccb0eea8a706c4c34a16891f84e7b
以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密
例如:
对字符串‘hello‘进行sha256加密,得到一个结果
对字符串‘hello‘,添加一个key(‘123‘),进行sha256加密
import hashlib
hash = hashlib.sha256()
hash.update(‘hello‘.encode(‘utf-8‘))
print(‘--->‘, hash.hexdigest())
hash = hashlib.sha256(‘hello‘.encode(‘utf-8‘))
hash.update(‘123‘.encode(‘utf8‘))
print(‘===>‘, hash.hexdigest())
#->
---> 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
===> 27cc6994fc1c01ce6659c6bddca9b69c4c6a9418065e612c69d110b3f7b11f8a
撞库模拟
库,可以理解为一个密码字典,里面记录了一些常用的密码,用于遍历每一个密码去检测密码是否匹配正确的密码
import hashlib
passwds=[
‘alex3714‘,
‘alex1313‘,
‘alex94139413‘,
‘alex123456‘,
‘123456alex‘,
‘a123lex‘,
]
def make_passwd_dic(passwds):
dic = {}
for passwd in passwds:
m = hashlib.md5()
m.update(passwd.encode(‘utf-8‘))
dic[passwd] = m.hexdigest()
return dic
def break_code(cryptograph,passwd_dic):
"""
cryptograph:为抓取到的密文密码
:param cryptograph:
:param passwd_dic:
:return:
"""
for k, v in passwd_dic.items():
if v == cryptograph:
print(‘密码是===>\033[46m%s\033[0m‘ %k)
cryptograph = ‘aee949757a2e698417463d47acac93df‘
#此处模拟alex3714经过md5算法产生的密文密码break_code(cryptograph, make_passwd_dic(passwds))
运行结果:
密码是===>alex3714
以上是关于15-常用模块的主要内容,如果未能解决你的问题,请参考以下文章
VSCode自定义代码片段15——git命令操作一个完整流程