内容
- Python模块详解
- import 使用
- time 与date time模块讲解
- random 模块讲解
- os 模块讲解
- sys模块和shutil模块讲解
- json、pickle、shelve模块讲解
- XML处理模块讲解
- PyYAML和configparse模块讲解
- hashlib模块讲解
- logging模块讲解
- re正则表达式模块讲解
- subprocess 模块讲解
- 作业需求
1、模块定义:
类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。
如:os 是系统相关的模块;file是文件操作相关的模块
2、使用模块的好处:
- 程序可扩展性
- 减少程序代码
- 方便程序架构的更改
3、模块分类:
- 自定义模块
- 内置模块(又称为标准库)
- 开源模块
以下举例说明:
(1)、自定义模块
A、同级目录下,定义一个py文件
a、#同级目录下,定义一个py文件,为moduel.py,内容如下: def chen(name): print("hello",name) b、#起用一个新py文件进行,调用 import moduel #导入定义的模块, moduel.chen("chenchangqing") #执行模块中,子函数类型 输出: hello chenchangqing
注意:python 中每一个单独的文件,都可以被当做为模块。底层的代码级别 : 流程控制->函数->类->模块->包
B、使用 `if __name__ == '__main__'` 调用
def user(name): print("your name",name) def password(): print("your passwd:123456") def number(): print("your number:400-158-1678") if __name__ == '__main__': #定义入口 A = input("your name :") user(A) password() number() 输出: your name :chenchangqing your name chenchangqing your passwd:123456 your number:400-158-1678
(2)、内置模块
如:os 是系统相关的模块;file是文件操作相关的模块 ; sys 模块等都属于python内置模块, 本文后面会介绍常用的模块。
(3)、开源模块
a、下载安装
开源模块的安装,有两种方式 第一种,使用系统自带工具安装 1、yum 2、pip3 第二种,是用源码安装 下载源码 解压源码 进入目录 编译源码 python setup.py build 安装源码 python setup.py install 注:在使用源码安装时,需要使用到gcc编译和python开发环境,所以,需要先执行: yum install gcc yum install python-devel 安装成功后,模块会自动安装到 sys.path 中的某个目录中,如: /usr/lib/python3.5.1/site-packages/
b、模块导入
与自定义模块导入的方式一样,使用import 、from .. import ..
c、以paramiko 模块为例
paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实。
安装 paramiko 模块:
1、使用系统工具安装 pip3 install paramiko 或者 2、使用源码安装 # pycrypto,由于 paramiko 模块内部依赖pycrypto,所以先下载安装pycrypto # 下载安装 pycrypto wget http://files.cnblogs.com/files/wupeiqi/pycrypto-3.5.2.tar.gz tar -xvf pycrypto-3.5.2.tar.gz cd pycrypto-3.5.2 python setup.py build python setup.py install # 进入python环境,导入Crypto检查是否安装成功 # 下载安装 paramiko wget http://files.cnblogs.com/files/wupeiqi/paramiko-1.10.1.tar.gz tar -xvf paramiko-1.10.1.tar.gz cd paramiko-1.10.1 python setup.py build python setup.py install # 进入python环境,导入paramiko检查是否安装成功 $ python3.5 >>> import paramiko #不报错,说明已经导入成功
paramiko的使用:
A、执行命令 - 通过用户名、密码连接服务器
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname="110.29.69.31",port=22,username="root", password="UByXfdadfsfsd") stdin, stdout, stderr = ssh.exec_command("uptime") print (stdout.read()) ssh.close() 输出: b' 14:51:41 up 1 day, 2:37, 0 users, load average: 0.00, 0.00, 0.00\n'
B、执行命令 - 通过密钥连接服务器
import paramiko import os key_path =os.path.expanduser('/Users/mac/me/key/Identity') #定义密钥存放的路径 key_rsa = paramiko.RSAKey.from_private_key_file(key_path,password="chen!#%1203") #密钥如果需要密码,则需要添加 password="chen!#%1203" ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname="109.10.10.18", port=22, username="www",pkey=key_rsa) #pkey 是指定要读取的密钥 stdin, stdout,sdterr = ssh.exec_command('uptime') print (stdout.read()) ssh.close() 输出: b' 14:15:31 up 591 days, 22:54, 0 users, load average: 0.00, 0.01, 0.00\n' 备注: 这里的key是通过crt生成的设置密码的,RSA Key有密码的话,所以在这里应用为,否则将会报无法识别的RSA KEY。 key_path =os.path.expanduser('/Users/mac/me/key/Identity') key_rsa = paramiko.RSAKey.from_private_key_file(key_path,password="chen!#%1203") 而如果你用的是RSA的key,是用sh-keygen -t rsa来指定的,则不需要要添加password="chen!#%1203"
C、上传或者下载文件 - 通过用户名和密码
#上传: import os,sys import paramiko t = paramiko.Transport(("192.168.3.52",22)) t.connect(username="root", password="fafdaa") sftp = paramiko.SFTPClient.from_transport(t) sftp.put('/Users/mac/me/ssh1.py','/tmp/ssh1.py') #前一个源路径、后一个是目标路径 t.close() 查看服务器上信息比对: [root@VM_centos tmp]# pwd /tmp [root@VM_centos tmp]# ls cvm_init.log net_affinity.log setRps.log ssh1.py #下载: import os,sys import paramiko t = paramiko.Transport(("119.29.69.31",22)) t.connect(username="root", password="UByXcYZmVCIisHd5") sftp = paramiko.SFTPClient.from_transport(t) sftp.get('/tmp/ssh1.py','//Users/mac/me/sshbak.py') #前一个源路径、后一个是目标路径 t.close() 查看本地目录 CQ-Chen:me CQ_Chen$ pwd /Users/mac/me CQ-Chen:me CQ_Chen$ ls -l -rw-r--r-- 1 CQ_Chen staff 378 11 14 11:28 sshbak.py
D、上传或者下载文件 - 通过密钥
#上传 import paramiko import os key_path =os.path.expanduser('/Users/mac/me/key/Identity') key_rsa = paramiko.RSAKey.from_private_key_file(key_path,password="chen!#%1203") t = paramiko.Transport(("109.10.10.98",22)) t.connect(username="www",pkey=key_rsa) sftp = paramiko.SFTPClient.from_transport(t) sftp.put("/Users/mac/me/109.10.10.98.sh","/tmp/ssh.sh")#源路径、目标路径 t.close() 服务器查看 $ ls ssh.sh #下载 import paramiko import os key_path =os.path.expanduser('/Users/mac/me/key/Identity') key_rsa = paramiko.RSAKey.from_private_key_file(key_path,password="chen!#%1203") t = paramiko.Transport(("109.10.10.98",22)) t.connect(username="www",pkey=key_rsa) sftp = paramiko.SFTPClient.from_transport(t) sftp.get("/tmp/ssh.sh","/Users/mac/me/down.sh") t.close() 注意:sftp.get put 的使用
Python之所以应用越来越广泛,在一定程度上也依赖于其为程序员提供了大量的模块以供使用,如果想要使用模块,则需要导入。
导入模块有一下几种方法:
import module #可参考自定模块的方法 from module.xx.xx import xx from module.xx.xx import xx as rename from module.xx.xx import *
举例:
1 同目录下 2 #定义模块,名称为module.py 3 def user(name): 4 print("your name",name) 5 6 def password(): 7 print("your passwd:123456") 8 9 def number(): 10 print("your number:400-158-1678") 11 12 #在另外一个python 文件中进行调用,文件名为test.py 13 from module import user 14 15 user("Chen") #在这里直接调用了函数名的。如果导入模块使用的是 import module 则这一步为 module.user("Chen"),前面要加入模块的前缀 16 输出: 17 your name Chen
1 #按照上面定义的,模块跟调用py文件分布在不同的 目录,进行调用 2 def user(name): 3 print("your name",name) 4 def password(): 5 print("your passwd:123456") 6 7 def number(): 8 print("your number:400-158-1678") 9 10 11 #利用test.py进行调用 12 错误写法为 13 from moduel import user 14 15 user("Chen") 16 输出报错、找不到模块 17 Traceback (most recent call last): 18 File "/Users/mac/PycharmProjects/untitled2/51CTO/5day/test.py", line 4, in <module> 19 from moduel import user 20 ImportError: No module named 'moduel' 21 22 正确写法为: 23 import sys 24 sys.path.append("/Users/mac/PycharmProjects/untitled2/51CTO/5day1/") #导入模块所在的目录 25 from moduel import user 26 27 user("Chen") 28 输出: 29 your name Chen
目录位置如截图:
备注:在模块所在的目录里面增加__init__.py文件,里面可以写import时执行的代码,当然也可以留空就可以。这是一个引用的文件,如果有三层目录,则每一层目录都要添加此文件,否则调用模块文件报错。
导入模块其实就是让Python解释器去解释那个py文件
- 导入一个py文件,解释器解释该py文件
- 导入一个包,解释器解释该包下的 __init__.py 文件
导入模块是根据那个路径作为基准来进行查找的呢。下面距举例:
import sys print (sys.path) 输出: ['/Users/mac/PycharmProjects/untitled2/51CTO/5day', '/Users/mac/PycharmProjects/untitled2', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages'] 以上是查找模块的顺序
如果sys.path路径列表没有你想要的路径,可以通过 sys.path.append('路径') 添加。
1 import sys 2 import os 3 4 pre_path = os.path.abspath('../') 5 sys.path.append(pre_path)
1、time模块讲解
时间相关的操作,时间有三种表示方式:
- 时间戳 1970年1月1日之后的秒,通常来说,时间戳表示的是从1970年1月1日00:00:00开始至今的时间差按秒计算的值。即:time.time()返回的是float类型。
- 格式化的字符串 2014-11-11 11:11, 即:time.strftime('%Y-%m-%d')
- 结构化时间 包含了struct_time元组共九个元素:(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)等 time.struct_time 即:time.localtime()、time.gmtime()
三者的关系:
三者之间的转换:
先以当前时间为准,快速认识三种形式的时间:
import time print(time.time()) # 时间戳:1487130156.419527 print(time.strftime("%Y-%m-%d %X")) #格式化的时间字符串:'2017-11-15 11:40:53' print(time.localtime()) #本地时区的struct_time print(time.gmtime()) #UTC时区的struct_time
第一幅图的转换
<-----按图1转换时间--------------------> #localtime([secs]) 将一个时间戳转换为当前时区的struct_time。secs参数未提供,则以当前时间为准。 time.localtime() time.localtime(1473525444.037215) # gmtime([secs]) 和localtime()方法类似,gmtime()方法是将一个时间戳转换为UTC时区(0时区)的struct_time。 #mktime(t) #将一个struct_time转化为时间戳。如: print(time.mktime(time.localtime()))#1473525749.0 #strftime(format[, t]) 把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime() 返回转化为格式化的时间字符串。如果t未指定,将入time.localtime() 如果元组中任何一个元素越界,ValueError的错误将会被抛出。 print(time.strptime('2017-11-15 16:37:06', '%Y-%m-%d %X')) #time.strptime(string[, format]) #把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作。 print(time.strftime("%Y-%m-%d %X", time.localtime()))#2016-09-11 00:49:56 #在这个函数中,format默认为:"%a %b %d %H:%M:%S %Y"。 time.struct_time(tm_year=2017, tm_mon=5, tm_mday=5, tm_hour=16, tm_min=37, tm_sec=6,tm_wday=3, tm_yday=125, tm_isdst=-1)
按第二幅图转换:
#<--------------------------按图2转换时间----------> # asctime([t]) : 把一个表示时间的元组或者struct_time表示为这种形式:'Sun Jun 20 23:21:05 1993'。 # 如果没有参数,将会将time.localtime()作为参数传入。 print(time.asctime())#Sun Sep 11 00:43:43 2016 # ctime([secs]) : 把一个时间戳(按秒计算的浮点数)转化为time.asctime()的形式。如果参数未给或者为 # None的时候,将会默认time.time()为参数。它的作用相当于time.asctime(time.localtime(secs))。 print(time.ctime()) # Sun Sep 11 00:46:38 2016 print(time.ctime(time.time())) # Sun Sep 11 00:46:38 2016
格式化字符串的时间格式,可以参考help()文档,进行查询:
%a Locale’s abbreviated weekday name. %A Locale’s full weekday name. %b Locale’s abbreviated month name. %B Locale’s full month name. %c Locale’s appropriate date and time representation. %d Day of the month as a decimal number [01,31]. %H Hour (24-hour clock) as a decimal number [00,23]. %I Hour (12-hour clock) as a decimal number [01,12]. %j Day of the year as a decimal number [001,366]. %m Month as a decimal number [01,12]. %M Minute as a decimal number [00,59]. %p Locale’s equivalent of either AM or PM. (1) %S Second as a decimal number [00,61]. (2) %U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3) %w Weekday as a decimal number [0(Sunday),6]. %W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3) %x Locale’s appropriate date representation. %X Locale’s appropriate time representation. %y Year without century as a decimal number [00,99]. %Y Year with century as a decimal number. %z Time zone offset indicating a positive or negative time difference from UTC/GMT of the form +HHMM or -HHMM, where H represents decimal hour digits and M represents decimal minute digits [-23:59, +23:59]. %Z Time zone name (no characters if no time zone exists). %% A literal '%' character.
2、datetime 模块
主要做时间的加减使用
import datetime print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925 print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式 2016-08-19 print(datetime.datetime.now() ) print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时 print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分 c_time = datetime.datetime.now() print(c_time.replace(minute=3,hour=2)) #时间替换
1、Python中的random模块用于生成随机数,以下是经常用到的模块函数:
import random print(random.random()) #随机打印(0,1)之间的float数数值,大于0且小于1之间的小数 print(random.randint(1,3)) #[1,3] 表示大于等于1且小于等于3之间的整数 print(random.randrange(1,3)) #[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)
2、编写网站验证码:
import random checkcode = '' for i in range(6): currnet = random.randrange(0,4) #取大于等于0且小于4的整数,取出来 if currnet == i: tmp = chr(random.randint(65,90)) #chr 根据ascii码表进行打印英文字符 ,65-90表示大写字母从A-Z print(chr(65),chr(90)) print("tmp1:",tmp) else: tmp=random.randint(0,4) #大于等于0且小于等于4之间的整数 print("tmp2:",tmp) checkcode += str(tmp) print(checkcode) 输出: tmp2: 4 tmp2: 3 tmp2: 1 A Z tmp1: C tmp2: 0 tmp2: 0 431C00 备注:注意一下输出方式
os模块是与操作系统交互的一个接口。
常用的模块函数如下:
在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。
liunx或Mac >>> os.getcwd()#获取当前路径 '/Users/mac' >>> os.chdir("/Users/mac/me") #更改所在目录 >>> os.getcwd() '/Users/mac/me' >>> os.path.normcase("/Users/mac/me") #返回所在目录 '/Users/mac/me' windows >>> os.path.normcase('c:/windows\\system32\\') 'c:\\windows\\system32\\' 去到其他的目录,或者使用 r >>>os.chdir(r'c:\windows\system32\chen') 理解 规范化路径,如..和/ >>> 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__))))
1、SYS常用的系统模块
1 sys.argv: 实现从程序外部向程序传递参数。 2 3 sys.exit([arg]): 程序中间的退出,arg=0为正常退出。 4 5 sys.getdefaultencoding(): 获取系统当前编码,一般默认为ascii。 6 7 sys.setdefaultencoding(): 设置系统默认编码,执行dir(sys)时不会看到这个方法,在解释器中执行不通过,可以先执行reload(sys),在执行 setdefaultencoding('utf8'),此时将系统默认编码设置为utf8。(见设置系统默认编码 ) 8 9 sys.getfilesystemencoding(): 获取文件系统使用编码方式,Windows下返回'mbcs',mac下返回'utf-8'. 10 11 sys.path: 获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到。 12 13 sys.platform: 获取当前系统平台。 14 sys.version 获取Python解释程序的版本信息 15 sys.maxint 最大的Int值 16 17 sys.stdin,sys.stdout,sys.stderr: stdin , stdout , 以及stderr 变量包含与标准I/O 流对应的流对象. 如果需要更好地控制输出,而print 不能满足你的要求, 它们就是你所需要的. 你也可以替换它们, 这时候你就可以重定向输出和输入到其它设备( device ), 或者以非标准的方式处理它们
sys.argv
功能:在外部向内部传递参数。举例:
$ cat test.py import sys print (sys.argv[0]) #默认获取脚本的明名称 print (sys.argv[1]) #获取外部传入的第一个参数 print (sys.argv[2]) #获取外部传入的第二个参数 print (sys.argv[3]) #获取外部传入的第三个参数 $ python3.5 test.py chen chang qing # 以空格为分隔符,进行区分传入 test.py chen chang qing
sys.exit(n)
功能:执行到主程序末尾,解释器自动退出,但是如果需要中途退出程序,可以调用sys.exit函数,带有一个可选的整数参数返回给调用它的程序,表示你可以在主程序中捕获对sys.exit的调用。(0是正常退出,其他为异常)
第一个方法: import sys def exitfun(): print("hello world") sys.exitfunc = exitfun() print ("hello") sys.exit(0) # 退出程序,不执行下面的操作 print ("there") 输出: hello world hello 第一个方法: import sys print ("chen1203") try: sys.exit(1) except SystemExit: #捕获退出的异常 pass #捕获后,不做任何的操作 print("This is OK!!") 输出: chen1203 This is OK!!
sys.path
功能:获取指定模块搜索路径的字符串集合,可以将写好的模块放在得到的某个路径下,就可以在程序中import时正确找到。
import sys print(sys.path) 输出: ['/Users/mac/PycharmProjects/untitled2/51CTO/5day', '/Users/mac/PycharmProjects/untitled2', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload', '/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages'] 使用的模块路径,不在默认路径里面,可以按下面方法执行: sys.path.append("路径") import 模块
sys.modules
功能:sys.modules
是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules
将自动记录该模块。当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。它拥有字典所拥有的一切方法。
1 >>> import sys 2 # 这个时候os这个module已经被加载了,但是在当前作用域并不可见。 3 >>> sys.modules['os'] 4 <module 'os' from '/usr/lib/python3.5/os.pyc'>
实现进度条的打印:
#指定宽度 print('[%-15s]' %'#') #15s 指定宽度 print('[%-15s]' %'##') print('[%-15s]' %'###') print('[%-15s]' %'####') #打印% print('%s%%' %(100)) #第二个%号代表取消第一个%的特殊意义 #可传参来控制宽度 print('[%%-%ds]' %50) #[%-50s] print(('[%%-%ds]' %50) %'#') print(('[%%-%ds]' %50) %'##') print(('[%%-%ds]' %50) %'###')
实例:
import sys import time def progress(percent,width=50): if percent >= 1: percent=1 show_str=('[%%-%ds]' %width) %(int(width*percent)*'#') print('\r%s %d%%' %(show_str,int(100*percent)),file=sys.stdout,flush=True,end='') data_size=1025#设计总数 recv_size=0 while recv_size < data_size: time.sleep(0.1) #模拟数据的传输延迟,注意时间间隔的设置 recv_size += 1 #每次累计增加1个 percent=recv_size/data_size #接收的比例 progress(percent,width=100) #进度条的宽度70 输出: [########################### ] 39%
2、shutil常用的系统模块
功能:高级的 文件、文件夹、压缩包 处理模块。常用作文件copy 。
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中
import shutil
shutil.copyfileobj(open('/Users/mac/me/ssh.sh',"r"),open("/Users/mac/me/chen.sh","w"))
shutil.copyfile(src, dst)
拷贝文件
1 shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在
shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变
1 shutil.copymode('f1.log', 'f2.log') #目标文件必须存在
shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
1 shutil.copystat('f1.log', 'f2.log') #目标文件必须存在
shutil.copy(src, dst)
拷贝文件和权限
1 import shutil 2 3 shutil.copy('f1.log', 'f2.log')
shutil.copy2(src, dst)
拷贝文件和状态信息
1 import shutil 2 3 shutil.copy2('f1.log', 'f2.log')
shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
递归的去拷贝文件夹:
1 import shutil
2
3 shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除
软连接的拷贝:
1 import shutil 2 3 shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))
shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件
1 import shutil 2 3 shutil.rmtree('folder1')
shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。
1 import shutil 2 3 shutil.move('folder1', 'folder3')
shutil.make_archive(base_name, format,...)
创建压缩包并返回文件路径,例如:zip、tar
创建压缩包并返回文件路径,例如:zip、tar
- base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
- 如 data_bak =>保存至当前路径
- 如:/tmp/data_bak =>保存至/tmp/
- format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
- root_dir: 要压缩的文件夹路径(默认当前目录)
- owner: 用户,默认当前用户
- group: 组,默认当前组
- logger: 用于记录日志,通常是logging.Logger对象
1 #将 /data 下的文件打包放置当前程序目录 2 import shutil 3 ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data') 4 5 6 #将 /data下的文件打包放置指定到/tmp/目录 7 import shutil 8 ret = shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
ZipFile
1 import zipfile 2 #压缩 3 z = zipfile.ZipFile("/Users/mac/me/key.zip","w") 4 z.write("/Users/mac/me/ssh.py") 5 z.close() 6 #解压 7 z = zipfile.ZipFile("/Users/mac/me/key.zip", 'r') 8 z.extractall(path='.') 9 z.close()
TarFile
1 import tarfile 2 3 # 压缩 4 t=tarfile.open('/tmp/egon.tar','w') #压缩包名字、路径定义 5 t.add('/test1/a.py',arcname='a.bak') #添加文件到压缩包里面 6 t.add('/test1/b.py',arcname='b.bak') 7 t.close() 8 9 # 解压 10 t=tarfile.open('/tmp/egon.tar','r') 11 t.extractall('/egon') 12 t.close()
七、json、pickle、shelve模块讲解
json、pickle模块参考,第4周 序列化与反序列化
shelve模块讲解
shelve是一额简单的数据存储方案,他只有一个函数就是open(),这个函数接收一个参数就是文件名,然后返回一个shelf对象,你可以用他来存储东西,就可以简单的把他当作一个字典,当你存储完毕的时候,就调用close函数来关闭。
1 import shelve 2 3 f = shelve.open(r'/Users/mac/me/123.py') 4 f["chen"] = ["chen","chang","qing"] 5 f["chen"].append("ok") 6 print (f["chen"]) 7 输出: 8 ['chen', 'chang', 'qing'] 9 10 11 存储的OK到哪里去了呢?其实很简单,OK没有写回,你把['chen', 'chang', 'qing']存到了f["chen"],当你再次读取f["chen"]的时候,f["chen"]只。 是一个拷贝,而你没有将拷贝写回,所以当你再次读取f["chen"]的时候,它又从源中读取了一个拷贝,所以,你新修改的内容并不会出现在拷贝中,解决的办法就是,第一个是利用一个缓存的变量,如下所示 12 13 temp = f["chen"] 14 temp.append("chen1203") 15 f["chen"] = temp 16 17 print (f["chen"]) 18 输出: 19 ['chen', 'chang', 'qing', 'chen1203']
xml即可扩展标记语言,它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。从结构上,很像HTML超文本标记语言。但他们被设计的目的是不同的,超文本标记语言被设计用来显示数据,其焦点是数据的外观。它被设计用来传输和存储数据,其焦点是数据的内容。那么Python是如何处理XML语言文件的呢?下面一起来看看Python常用内置模块之xml模块吧。
本文主要学习的ElementTree是python的XML处理模块,它提供了一个轻量级的对象模型。在使用ElementTree模块时,需要import xml.etree.ElementTree的操作。ElementTree表示整个XML节点树,而Element表示节点数中的一个单独的节点。
设定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文件进行读取:
1 import xml.etree.ElementTree as ET 2 3 4 tree = ET.parse("xmltest.xml") 5 root = tree.getroot() 6 print(root.tag) # 打印了标签 7 8 print ("====================") 9 10 #遍历xml文档内容 11 for child in root: 12 print(child.tag, child.attrib) 13 for i in child: 14 print (i.tag, i.text) 15 16 print("====================") 17 18 #只遍历year节点 19 for node in root.iter("year"): 20 print(node.tag, node.text) 21 22 输出: 23 data 24 ==================== 25 country {'name': 'Liechtenstein'} 26 rank 2 27 year 2008 28 gdppc 141100 29 neighbor None 30 neighbor None 31 country {'name': 'Singapore'} 32 rank 5 33 year 2011 34 gdppc 59900 35 neighbor None 36 country {'name': 'Panama'} 37 rank 69 38 year 2011 39 gdppc 13600 40 neighbor None 41 neighbor None 42 ==================== 43 year 2008 44 year 2011 45 year 2011
对xml文档内容修改:
1 import xml.etree.ElementTree as ET 2 3 tree = ET.parse("xmltest.xml") 4 root = tree.getroot() 5 6 #修改 7 8 for node in root.iter("year"): #读取的是内存地址 9 new_year = int(node.text) + 1#转换为数字类型 +1 10 node.text = str(new_year) 11 node.set("updated","yes") #指定需要修改的边标签值 12 13 tree.write("xmltest.xml") 14 15 #查看文件 16 <data> 17 <country name="Liechtenstein"> 18 <rank updated="yes">2</rank> 19 <year updated="yes">2012</year> #在原来的基础上,加了1 20 <gdppc>141100</gdppc> 21 <neighbor direction="E" name="Austria" /> 22 <neighbor direction="W" name="Switzerland" /> 23 </country> 24 <country name="Singapore"> 25 <rank updated="yes">5</rank> 26 <year updated="yes">2015</year> 27 <gdppc>59900</gdppc> 28 <neighbor direction="N" name="Malaysia" /> 29 </country> 30 <country name="Panama"> 31 <rank updated="yes">69</rank> 32 <year updated="yes">2015</year> 33 <gdppc>13600</gdppc> 34 <neighbor direction="W" name="Costa Rica" /> 35 <neighbor direction="E" name="Colombia" /> 36 </country> 37 </data>
对xml文档内容删除:
1 import xml.etree.ElementTree as ET 2 3 tree = ET.parse("xmltest.xml") 4 root = tree.getroot() 5 6 7 #删除node 8 for country in root.findall("country"): 9 rank = int(country.find("rank").text) 10 print (rank) 11 if rank > 50: 12 root.remove(country) 13 tree.write("output.xml") 14 查看重新生成的文件: 15 <data> 16 <country name="Liechtenstein"> 17 <rank updated="yes">2</rank> 18 <year updated="yes">2012</year> 19 <gdppc>141100</gdppc> 20 <neighbor direction="E" name="Austria" /> 21 <neighbor direction="W" name="Switzerland" /> 22 </country> 23 <country name="Singapore"> 24 <rank updated="yes">5</rank> 25 <year updated="yes">2015</year> 26 <gdppc>59900</gdppc> 27 <neighbor direction="N" name="Malaysia" /> 28 </country> 29 </data> 30 31 把<rank updated="yes">69</rank>给删除了
创建xml文件
1 import xml.etree.ElementTree as ET 2 3 new_xml = ET.Element("namelist") #定义tag 4 name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"}) 5 age = ET.SubElement(name, "age", attrib={"checked": "no"}) 6 sex = ET.SubElement(name, "sex") 7 sex.text = '33' 8 name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"}) 9 age = ET.SubElement(name2, "age") 10 age.text = '19' 11 12 et = ET.ElementTree(new_xml) # 生成文档对象 13 et.write("test.xml", encoding="utf-8", xml_declaration=True) 14 15 ET.dump(new_xml) # 打印生成的格式 16 输出: 17 <namelist> 18 <name enrolled="yes"> 19 <age checked="no" /> 20 <sex>33</sex> 21 </name> 22 <name enrolled="no"> 23 <age>19</age> 24 </name> 25 </namelist>
九、PyYAML和configparse模块讲解
YAML:此模块默认没有安装,需要用到模块,编译安装
YAML是一种用来表达数据序列的编程语言,它的主要特点包括:可读性强、语法简单明了、支持丰富的语言解析库、通用性强等。Ansible与Saltstack环境中配置文件都以YAML格式存在。下面是saltstack的配置:
1 file_roots: 2 base: 3 - /srv/salt/ 4 dev: 5 - /srv/salt/dev 6 prod: 7 - /srv/salt
要通过YAML描述与Python的对应关系,从而方便读者了解YAML的层次及结构,最常见的是映射到Python中的列表(List)、字典(Dictionary)两种对象类型。下面通过块序列与块映射的示例详细说明。
1.块序列描述
块序列就是将描述的元素序列到Python的列表(List)中
1 #/!/usr/bin/env python 2 import yaml 3 data=yaml.load(""" 4 - one 5 - two 6 - three 7 - four 8 """) 9 print (data) 10 用“-”来分隔列表中的每个元素,运行结果如下: 11 # python yamltest.py 12 ['one', 'two', 'three', 'four'] 13 14 或 15 16 #/!/usr/bin/env python 17 import yaml 18 data=yaml.load(""" 19 - 20 - one 21 - two 22 - three 23 - four 24 - 25 - list 26 - dict 27 - set 28 """) 29 print (data) 30 显示如下: 31 python yamltest.py 32 [['one', 'two', 'three', 'four'], ['list', 'dict', 'set']] 33 #返回的是list类型
2.块映射描述
块映射就是将描述的元素序列到Python的字典(Dictio-nary)中,格式为“键(key):值(value)”。
1 #/!/usr/bin/env python 2 3 import yaml 4 data=yaml.load(""" 5 hero: 6 me: 21 7 mayun: 55 8 """) 9 print (type(data)) 10 print (data) 11 结果: 12 <type 'dict'> 13 {'hero': {'me': 21, 'mayun': 55}}
YAML块序列与块映射是可以自由组合在一起的,它们之间可以相互嵌套,通过非常灵活的组合,可以帮助我们描述更加复杂的对象属性
1 #注意空格和缩进 2 #/!/usr/bin/env python 3 4 import yaml 5 data=yaml.load(""" 6 - hero: 7 me: 21 8 mayun: 55 9 - name: 10 ME: 11 - 12 12 - 33 13 """) 14 print (data) 15 结果: 16 [{'hero': {'me': 21, 'mayun': 55}}, {'name': {'ME': [12, 33]}}]
案例:
1 新建install.yaml文件 2 输入一下内容 3 name: Tom Smith 4 age: 37 5 spouse: 6 name: Jane Smith 7 age: 25 8 children: 9 - name: Jimmy Smith 10 age: 15 11 - name1: Jenny Smith 12 age1: 12 13 14 #yaml读 15 import yaml 16 with open('disk.sls')as f: 17 s=yaml.load(f) 18 print(s) 19 {'spouse': {'name': 'Jane Smith', 'age': 25}, 'name': 'Tom Smith', 'children': [{'name': 'Jimmy Smith', 'age': 15}, {'age1': 12, 'name1': 'Jenny Smith'}], 'age': 37} 20 ============================================ 21 22 将字符串转入yaml格式放入yaml文件 23 file='test.yaml' 24 data={'host': {'ip01': {'two': '192.168.1.254', 'one': '192.168.1.2'}, 'ip00': '192.168.1.1'}, 'soft': {'apache': 2.2, 'php': 5.3, 'mysql': 5.2}} 25 f=open(file,'w') 26 yaml.dump(data,f) 27 f.close()
ConfigParser模块
用于生成和修改常见配置文档,如常见的php.ini 文件、my.conf 文件。当前模块的名称在 python 3.x 版本中变更为 configparser。
配置文件的格式是: []包含的叫section, section 下有option=value这样的键值
举例:
config.txt 文件测试操作:
1 [section1] 2 name = tank 3 age = 28 4 5 [section2] 6 ip = 192.168.1.1 7 port = 8080
模拟增、删、改、查
1 import configparser 2 3 conf = configparser.ConfigParser() 4 conf.read("config.txt") 5 6 # 获取指定的section1, 指定的option的值 7 name = conf.get("section1","name") 8 print(name) 9 age = conf.get("section1","age") 10 print(age) 11 12 print("========================") 13 14 #获取所有的section 15 setions_all = conf.sections() 16 print(setions_all) 17 18 print("========================") 19 20 #更改配置文件 21 #更新指定的值, 此时是更新内存中的值,并非更改文件中的值 22 conf.set("section1","age","30") 23 age_charge = conf.get("section1","age") 24 print(age_charge) 25 26 print("=========================") 27 28 # 写入指定section, 增加新option的值 29 conf.set("section2", "IEPort", "8900") 30 IEPort = conf.get("section2","IEPort") 31 print(IEPort) 32 33 print("=========================") 34 35 # 添加新的 section 36 conf.add_section("section3") 37 conf.set("section3", "URL", "http://www.baidu.com") 38 sections_URL = conf.get("section3", "URL") 39 print(sections_URL) 40 41 print("=========================") 42 43 # 写回配置文件 44 conf.write(open("config.conf","w")) ###指定写到一个新文件中,如果更改的是原文件,指定原来的文件操作 45 46 输出: 47 48 tank 49 28 50 ======================== 51 ['section1', 'section2'] 52 ======================== 53 30 54 ========================= 55 8900 56 ========================= 57 http://www.baidu.com 58 =========================
新文件效果:
1 [section1] 2 name = tank 3 age = 30 4 5 [section2] 6 ip = 192.168.1.1 7 port = 8080 8 ieport = 8900 9 10 [section3] 11 url = http://www.baidu.com
hashlib 模块主要用途用于加密线相关的操作,3版本里面替代md5和sha模块,主要提供SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法。
举例说明:
import hashlib hash_md5 = hashlib.md5() hash_md5.update(bytes('admin',encoding='utf-8')) print (hash_md5.digest()) #二进制hash print (hash_md5.hexdigest()) #16进制格式hash print ("================================") #hsha1 hash_sha1 = hashlib.sha1() hash_sha1.update(b"admin") print (hash_sha1.digest()) print (hash_sha1.hexdigest()) print ("================================") #sha2 hash_sha2 = hashlib.sha256() hash_sha2.update(b"admin") print (hash_sha2.digest()) print (hash_sha2.hexdigest()) print ("================================") #sha3 hash_sha2 = hashlib.sha384() hash_sha2.update(b"admin") print (hash_sha2.digest()) print (hash_sha2.hexdigest()) print ("================================") #sha4 hash_sha4 = hashlib.sha384() hash_sha4.update(b"admin") print (hash_sha4.digest()) print (hash_sha4.hexdigest()) print ("================================") #sha5 hash_sha5 = hashlib.sha512() hash_sha5.update(b"admin") print (hash_sha5.digest()) print (hash_sha5.hexdigest()) 输出; b'!#/)zW\xa5\xa7C\x89J\x0eJ\x80\x1f\xc3' 21232f297a57a5a743894a0e4a801fc3 ================================ b'\xd03\xe2*\xe3H\xae\xb5f\x0f\xc2\x14\n\xec5\x85\x0cM\xa9\x97' d033e22ae348aeb5660fc2140aec35850c4da997 ================================ b'\x8civ\xe5\xb5A\x04\x15\xbd\xe9\x08\xbdM\xee\x15\xdf\xb1g\xa9\xc8s\xfcK\xb8\xa8\x1fo*\xb4H\xa9\x18' 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 ================================ b'\x9c\xa6\x94\xa9\x02\x85\xc04C,\x95PB\x1b{\x9d\xbd\\\x0fKfs\xf0_m\xbc\xe5\x80R\xba \xe4$\x80A\x95n\xe8\xc9\xa2\xec\x9f\x10)\x0c\xdc\x07\x82' 9ca694a90285c034432c9550421b7b9dbd5c0f4b6673f05f6dbce58052ba20e4248041956ee8c9a2ec9f10290cdc0782 ================================ b'\x9c\xa6\x94\xa9\x02\x85\xc04C,\x95PB\x1b{\x9d\xbd\\\x0fKfs\xf0_m\xbc\xe5\x80R\xba \xe4$\x80A\x95n\xe8\xc9\xa2\xec\x9f\x10)\x0c\xdc\x07\x82' 9ca694a90285c034432c9550421b7b9dbd5c0f4b6673f05f6dbce58052ba20e4248041956ee8c9a2ec9f10290cdc0782 ================================ b'\xc7\xadD\xcb\xadv*]\xa0\xa4R\xf9\xe8T\xfd\xc1\xe0\xe7\xa5*8\x01_#\xf3\xea\xb1\xd8\x0b\x93\x1d\xd4rcM\xfa\xc7\x1c\xd3N\xbc5\xd1j\xb7\xfb\x8a\x90\xc8\x1f\x97Q\x13\xd6\xc7S\x8d\xc6\x9d\xd8\xde\x90w\xec' c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec
Python 还有一个hmac模块,内部是对创建的 key 和 内容,进行处理然后再加密。
散列消息鉴别码,简称HMAC,是一种基于消息鉴别码MAC(Message Authentication Code)的鉴别机制。使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥K来鉴别消息的真伪;
一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。
举例:
1 import hmac 2 hash_hamc = hmac.new(b"Are you OK ", b"Yes") 3 print(hash_hamc.digest()) 4 print(hash_hamc.hexdigest()) 5 6 输出: 7 8 b'@\xc1\x97\xd9q\x0c\x14\xd9\x12\xcf\xbf\xa2\xfb\xc9\xf3L' 9 40c197d9710c14d912cfbfa2fbc9f34c
md5 加密案例:
md5对象,md5不能反解,但是加密是固定的,就是关系是一一对应,所以有缺陷,可以被对撞出来。
1 #hashlib简单使用 2 def md5(arg):#这是加密函数,将传进来的函数加密 3 md5_pwd = hashlib.md5(bytes('abd',encoding='utf-8')) 4 md5_pwd.update(bytes(arg,encoding='utf-8')) 5 return md5_pwd.hexdigest()#返回加密的数据 6 def log(user,pwd):#登陆时候时候的函数,由于md5不能反解,因此登陆的时候用正解 7 with open('db','r',encoding='utf-8') as f: 8 for line in f: 9 u,p=line.strip().split('|') 10 if u ==user and p == md5(pwd):#登陆的时候验证用户名以及加密的密码跟之前保存的是否一样 11 return True 12 def register(user,pwd):#注册的时候把用户名和加密的密码写进文件,保存起来 13 with open('db','a',encoding='utf-8') as f: 14 temp = user+'|'+md5(pwd) 15 f.write(temp) 16 17 i=input('1表示登陆,2表示注册:') 18 if i=='2': 19 user = input('用户名:') 20 pwd =input('密码:') 21 register(user,pwd) 22 elif i=='1': 23 user = user = input('用户名:') 24 pwd =input('密码:') 25 r=log(user,pwd)#验证用户名和密码 26 if r ==True: 27 print('登陆成功') 28 else: 29 print('登陆失败') 30 else: 31 print('账号不存在')
程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug()
, info()
, warning()
, error()
and critical() 5个级别
import logging logging.debug("This is debug message") logging.info("This is info message") logging.warning("This is warning message") logging.error("This is error message") logging.critical("This is critical message") 输出: WARNING:root:This is warning message ERROR:root:This is error message CRITICAL:root:This is critical message
屏幕输出,只有CRITICAL\ERROR\ WARNING三种格式的日志进行输出。
默认情况下,logging将日志打印到屏幕,日志级别默认为WARNING;
日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET,当然也可以自己定义日志级别。
5种日志的定义:
DEBUG 详细信息,通常只有在诊断问题时才有意义。
INFO 信息确认事情按预期工作。
WARNING 指示意外事件发生,或在不久的将来指示某些问题(例如“磁盘空间不足”)。该软件仍在按预期工作。
ERROR 由于更严重的问题,软件不能执行某些功能。
CRITICAL 严重错误,表示程序本身可能无法继续运行
1、把日志记录到文件里面
import logging logging.basicConfig(filename='logging.log',level=logging.INFO) logging.debug('This is debug message') logging.info('This is info message') logging.warning('This is warning message') logging.error('This is ERROR message') logging.critical('This is critical message') 查看文件记录: INFO:root:This is info message WARNING:root:This is warning message ERROR:root:This is ERROR message CRITICAL:root:This is critical message
备注:level=loggin.INFO 意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里。顺序大小为(日志级别大小关系为:CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET),如果要记录debug日志,只要设置level=logging.INFO 为level=logging.DEBUG
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参数会被忽略。
2、对日志的输出,带上时间格式
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S %p', filename='logging.log') logging.error("This is error message") 检查文件保存: 2017-11-28 16:18:40 PM This is error message
format='%(asctime)s %(message)s'定义的格式如下:
%(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 | 用户输出的消息 |
3、进阶的使用
主要分为四个部分:
- Loggers:提供应用程序直接使用的接口
- Handlers:将Loggers产生的日志传到指定位置
- Filters:对输出日志进行过滤
- Formatters:控制输出格式
当项目中使用logging模块的时候肯定不能在这样一句句的写了,一般可能会抽象出一个模块来,这样比较有效一些。logging模块提供了四个类(Loggers,Formatters,Filtters,Handlers)来实现不同的功能。
举例,把日志输出到屏幕上:
1 import logging 2 3 #创建 logger 4 logger = logging.getLogger("my_log") 5 logger.setLevel(logging.DEBUG) 6 7 #创建克隆handler 并设置日志级别为debug 8 ch = logging.StreamHandler() 9 ch.setLevel(logging.DEBUG) 10 11 #创建 formatter,控制日志输出格式 12 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 13 14 #把formatter信息添加到ch 15 ch.setFormatter(formatter) 16 17 #把ch 信息添加到 logger 18 logger.addHandler(ch) 19 20 #举例 21 logger.debug("This is debug message") 22 logger.info("This is Info message") 23 logger.warning("This is Warning message") 24 logger.error("This is Error message") 25 26 输出: 27 28 2017-11-29 12:06:10,919 - my_log - DEBUG - This is debug message 29 2017-11-29 12:06:10,920 - my_log - INFO - This is Info message 30 2017-11-29 12:06:10,920 - my_log - WARNING - This is Warning message 31 2017-11-29 12:06:10,920 - my_log - ERROR - This is Error message
把日志输出到屏幕、同时保存在文件中:
1 import logging 2 3 #创建 logger 4 logger = logging.getLogger("my_log") 5 logger.setLevel(logging.DEBUG) 6 7 #创建克隆handler 并设置日志级别为debug 8 ch = logging.StreamHandler() 9 ch.setLevel(logging.DEBUG) 10 11 fh = logging.FileHandler("my_access.log") 12 fh.setLevel((logging.DEBUG)) 13 14 #创建 formatter 15 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') 16 17 #把formatter信息添加到ch、fh 18 ch.setFormatter(formatter) 19 fh.setFormatter(formatter) 20 21 #把ch、fh 信息添加到 logger 22 logger.addHandler(ch) 23 logger.addHandler(fh) 24 25 #举例 26 logger.debug("This is debug message") 27 logger.info("This is Info message") 28 logger.warning("This is Warning message") 29 logger.error("This is Error message") 30 31 输出: 32 2017-11-29 12:24:06,000 - my_log - DEBUG - This is debug message 33 2017-11-29 12:24:06,001 - my_log - INFO - This is Info message 34 2017-11-29 12:24:06,001 - my_log - WARNING - This is Warning message 35 2017-11-29 12:24:06,001 - my_log - ERROR - This is Error message
logging模块架构的文字解析:
Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适: logger提供了应用程序可以直接使用的接口; handler将(logger创建的)日志记录发送到合适的目的输出; filter提供了细度设备来决定输出哪条日志记录; formatter决定日志记录的最终输出格式。 logger 每个程序在输出信息之前都要获得一个Logger。 Logger通常对应了程序的模块名 如比如聊天工具的图形界面模块可以这样获得它的Logger: LOG=logging.getLogger(”chat.gui”) 核心模块表示: LOG=logging.getLogger(”chat.kernel”) 指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高: Logger.setLevel(lel) 添加或删除指定的filter: Logger.addFilter(filt) Logger.removeFilter(filt) 增加或删除指定的handler: Logger.addHandler(hadr) Logger.removeHandler(hadr) 可以设置的日志级别: Logger.debug() Logger.info() Logger.warning() Logger.error() Logger.critical() ================================= 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 每天凌晨
TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])
其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。
interval是时间间隔。
when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:
S 秒
M 分
H 小时
D 天
W 每星期(interval==0时代表星期一)
midnight 每天凌晨
如下:
import logging from logging import handlers logger = logging.getLogger(__name__) log_file = "timelog.log" fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3) formatter = logging.Formatter('%(asctime)s %(module)s:%(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test1") logger.warning("test12") logger.warning("test13") logger.warning("test14")
十二、re正则表达式模块讲解
正则表达式:python 中为re 模块,正则表达式中,常用的函数为 :match() 、search()、sub()、subn()、split()、findall()
功能用法:
1 re.match 从头开始匹配 2 re.search 匹配包含 3 re.findall 把所有匹配到的字符放到以列表中的元素返回 4 re.splitall 以匹配到的字符当做列表分隔符 5 re.sub 匹配字符并替换
以下为正则表达式中,常用语法
练习:
import re #匹配文本中每个邮箱的,按照列表的形式打印 y = "123@qq.comchen@gmail.comchen@163.comqing@126.com" res = re.findall('\w+@(?:qq|gmail|163|126).com',y) # print(res) print ("=================================") #匹配一段文本中的每行的时间字符串,并按字典的形式输出 time = 'afafahsdjfhke0adshf1980-09-12asdfhh-0127889sdhflajhfw' res_time1 = re.search(r'(?P<year>19[0-9]\d)-(?P<month>\d+)-(?P<days>\d+)',time).groupdict() res_time2 = re.search(r'(?P<year>19[0-9]\d)-(?P<month>\d+)-(?P<days>\d+)',time).group() res_time3 = re.search(r'(?P<year>19[0-9]\d)-(?P<month>\d+)-(?P<days>\d+)',time).groups() print(res_time1) print(res_time2) print(res_time3) #对比一下,使用groups、group、groupdict 的输出格式 print ("=================================") #匹配一段文本中所有的身份证数字 a = '121414242,124331242423412413,2324123424,5674576,3522425,afsesdf' res_auth = re.findall('\d{18}',a) print(res_auth) print ("=================================") #匹配QQ号 b = '1002040,10201,23,23124234,12312408' res_b0 = re.findall('\d\d{5,}',b) res_b1 = re.findall('[0-9][0-9]{5,}',b) print(res_b0) print(res_b1) #备注:\d 相等于 [0-9] 输出: ['123@qq.com', 'chen@gmail.com', 'chen@163.com', 'qing@126.com'] ================================= {'year': '1980', 'days': '12', 'month': '09'} 1980-09-12 ('1980', '09', '12') ================================= ['124331242423412413'] ================================= ['1002040', '23124234', '12312408'] ['1002040', '23124234', '12312408']
十三、subprocess 讲解
subprocess 模块是跟系统交互的模块(调用liunx系统命令用),是从python 2.4 版本中开始引入的模块。3.5版本后得到广泛使用,主要是用来取代一些旧的模块方法,如os.system、os.spwan*、os.popen*、commands.*等。subprocess 通过子进程来执行外部指令,并通过input/output/error 管道,获取子进程的执行的返回信息。
3.5版本中常用语法:
1、subprocess.call() #使用列表的方式,进行系统命令调用。在python3.5版本中,可以使用subprocess.run()(python2.*版本中,没有这个参数),两个参数结果显示一样。
1、调用系统命令 >>> import subprocess >>> subprocess.run(['df','-h']) #当做列表的形式,命令一个个传进去。 Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk1 112Gi 41Gi 70Gi 37% 970531 4293996748 0% / devfs 182Ki 182Ki 0Bi 100% 628 0 100% /dev map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home CompletedProcess(args=['df', '-h'], returncode=0) >>> subprocess.call(['df','-h']) Filesystem Size Used Avail Capacity iused ifree %iused Mounted on /dev/disk1 112Gi 41Gi 70Gi 37% 970531 4293996748 0% / devfs 182Ki 182Ki 0Bi 100% 628 0 100% /dev map -hosts 0Bi 0Bi 0Bi 100% 0 0 100% /net map auto_home 0Bi 0Bi 0Bi 100% 0 0 100% /home 0 2、命令中,如包含了管道符号,交由shell来处理。(两种模式均可使用) >>> subprocess.run('df -h |grep disk1', shell=True) #当调用命令中,有管道符号是,交由shell来处理 。 /dev/disk1 112Gi 41Gi 70Gi 37% 975722 4293991557 0% / CompletedProcess(args='df -h |grep disk1', returncode=0) >>> subprocess.call(args='df -h |grep disk1', shell=True) #执行命令,如果命令结果为0,就正常返回,否则抛异常 /dev/disk1 112Gi 41Gi 70Gi 37% 970581 4293996698 0% / 0
2、subprocess.getstatusoutput() 接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果
>>> subprocess.getstatusoutput(['ls /bin/ls'])
(0, '/bin/ls')
3、 subprocess.getoutput() #接收字符串格式命令,并返回结果(注意,返回结果,并不是打印)
>>> subprocess.getoutput('df -h |grep /dev')
'/dev/disk1 112Gi 41Gi 70Gi 37% 970645 4293996634 0% /\ndevfs 182Ki 182Ki 0Bi 100% 628 0 100% /dev'
>>> res=subprocess.check_output(['ls','-l']) #通过res赋值,在打印
>>> res
b'total 128\ndrwx------@ 3 CQ_Chen staff 102 12 26 10:49 Applications\ndrwx
调用subprocess.run(...):是推荐的常用方法,在大多数情况下能满足需求,但如果你可能需要进行一些复杂的与系统的交互的话,你还可以用subprocess.Popen()
4、subprocess.Popen() #在一些复杂场景中,我们需要将一个进程的执行输出作为另一个进程的输入。在另一些场景中,我们需要先进入到某个输入环境,然后再执行一系列的指令等。这个时候我们就需要使用到suprocess的Popen()方法。
可用参数:
args:shell命令,可以是字符串或者序列类型(如:list,元组)
bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
shell:是否使用shell来执行命令
cwd:用于设置子进程的当前目录
env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
universal_newlines:不同系统的换行符不同,True -> 同意使用 \n
startupinfo与createionflags只在windows下有效将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等
操作:
举例1:/Users/mac/me目录下建一个目录为chenchangqing。
p = subprocess.Popen('mkdir chenchangqing',shell=True,cwd='/Users/mac/me')
举例2:
import subprocess
res = subprocess.Popen("sleep 5;echo 'hello'",shell=True,stdin=subprocess.PIPE,stderr=subprocess.PIPE)
out1 = res.wait() #检查执行进度, 直到程序执行完成。0为正常、1为异常。
out2 = res.poll() #检查子进程状态,0为正常、1为抛出异常。
print (out1,out2)
举例3: 输入进行某环境,依赖再输入,如:python3(指定在3版本下去执行命令)
import subprocess
#指python环境下,指定python环境执行命令
obj = subprocess.Popen(["python"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
obj.stdin.write(b"print(1)\n")
obj.stdin.write(b"print(2)\n")
obj.stdin.write(b"print(3)\n")
obj.stdin.write(b"print(4)\n")
obj.stdin.close()
cmd_out = obj.stdout.read()
obj.stdout.close()
cmd_error = obj.stderr.read() #读取输出的错误日志
obj.stderr.close()
print (cmd_out)
print (cmd_error)
输出:
b'1\n2\n3\n4\n'
b''
举例4:输入即可得到输出(结果),如:ifconfig (也是按照列表,把命令拆分进行输入)
import subprocess
IP = subprocess.Popen(['ifconfig', 'en0'],stdin=subprocess.PIPE,stderr=subprocess.PIPE)
print(IP)
输出:
<subprocess.Popen object at 0x1007a5c18>
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
ether 18:65:90:d5:e3:51
inet6 fe80::1091:3900:1e4:73dc%en0 prefixlen 64 secured scopeid 0x4
inet 10.9.15.233 netmask 0xfffffc00 broadcast 10.9.15.255
nd6 options=201<PERFORMNUD,DAD>
media: autoselect
status: active
其他操作:
import subprocess
process = subprocess.Popen('sleep 60',shell=True,stdout=subprocess.PIPE)
process.poll() #检查子进程状态
process.kill() #终止子进程
process.send_signal() #向子进程发送信号
process.terminate() #终止子进程
十四、作业需求
开发一个简单的python计算器
- 实现加减乘除及拓号优先级解析
- 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
re.search(r'\([^()]+\)',s).group()