本节大纲:
- 模块和包
- time &datetime模块
- random
- os
- sys
- shutil
- json & picle
- shelve
- xml处理
- configparser
- hashlib
- subprocess
- logging模块
- re正则表达式
一、 模块和包 |
a.定义:
模块用来从逻辑上组织python代码(变量,函数,类,逻辑:实现一个功能),本质就是.py结尾的python文件。(例如:文件名:test.py,对应的模块名:test)
包:用来从逻辑上组织模块的,本质就是一个目录(必须带有一个__init__.py的文件)
b.导入方法
import module_name
import module_1的本质:是将module_1解释了一遍
也就是将module_1中的所有代码复制给了module_1
from module_name1 import name
本质是将module_name1中的name变量放到当前程序中运行一遍
所以调用的时候直接print(name)就可以打印出name变量的值
模块module_1.py代码:
1 name = "susu" 2 def say_hello(): 3 print("hello %s" %name) 4 调用模块的python程序main代码如下:(切记调用模块的时候只需要import模块名不需要加.py) 5 import module_1 6 7 #调用变量 8 print(module_1.name) 9 10 #调用模块中的方法 11 module_1.say_hello()
import module_name1,module_name2
from module_name import *(这种方法不建议使用)
from module_name import logger as log(别名的方法)
c.导入模块的本质就是把python文件解释一遍
import module_name---->module_name.py---->module_name.py的路径---->sys.path
导入包的本质就是执行该包下面的__init__.py
关于导入包的一个代码例子:
新建一个package_test包,并在该包下面建立一个test1.py的python程序,在package包的同级目录建立一个p_test.py的程序
test1的代码如下:
1 def test(): 2 print("int the test1")
package_test包下的__init__.py的代码如下:
1 #import test1 (理论上这样就可以但是在pycharm下测试必须用下面from .import test1) 2 from . import test1 3 print("in the init")
p_test的代码如下:
1 import package_test #执行__init__.py 2 package_test.test1.test()
这样运行p_test的结果:
1 D:\python35\python.exe D:/python培训/s14/day5/p_test.py 2 in the init 3 int the test1 4 5 Process finished with exit code 0
从上述的例子中也可以看出:导入包的时候其实是执行包下的__init__.py程序,所以如果想要调用包下面的python程序需要在包下的__init__.py导入包下面的程序
c.模块的分类
- 自定义模块
- 内置标准模块(又称标准库)
- 开源模块
自定义模块 和开源模块的使用参考 http://www.cnblogs.com/wupeiqi/articles/4963027.html
二、time &datetime模块 |
time与datetime
python中常见的时间表示方法:
a. 时间戳
时间戳:从1970年1月1日00:00:00到现在为止一共的时间数(单位为秒)
>>> time.time()
1495008200.629991
b. 格式化的时间字符串
c. struct_time(元组)
相互之间的转换关系如下:
1) time.localtime()将时间戳转换为当前时间的元组
>>> time.localtime()
time.struct_time(tm_year=2016, tm_mon=8, tm_mday=24, tm_hour=13, tm_min=27, tm_sec=55, tm_wday=2, tm_yday=237, tm_isdst=0)
>>>
2) time.gmtime()将时间戳转换为当前时间utc时间的元组
>>> time.gmtime()
time.struct_time(tm_year=2016, tm_mon=8, tm_mday=24, tm_hour=5, tm_min=35, tm_sec=43, tm_wday=2, tm_yday=237, tm_isdst=0)
>>>
3) time.mktime()可以将struct_time转换成时间戳
>>> x = time.localtime()
>>> x
time.struct_time(tm_year=2016, tm_mon=8, tm_mday=24, tm_hour=13, tm_min=39, tm_sec=42, tm_wday=2, tm_yday=237, tm_isdst=0)
>>> time.mktime(x)
1495010622.0
>>>
4) time.strftime将struct_time(元祖)装换成格式化的时间字符串
格式:time.strftime("%Y-%m-%d %H:%M:%S",x)
>>> x
time.struct_time(tm_year=2016, tm_mon=8, tm_mday=24, tm_hour=13, tm_min=39, tm_sec=42, tm_wday=2, tm_yday=237, tm_isdst=0)
>>> time.strftime("%Y-%m-%d %H:%M:%S",x)
'2016-08-24 13:39:42'
>>>
5) time.strptime 可以将格式化的时间字符串转换为struct_time
>>> time.strptime("2016-08-24 14:05:32","%Y-%m-%d %H:%M:%S")
time.struct_time(tm_year=2016, tm_mon=8, tm_mday=24, tm_hour=14, tm_min=5, tm_sec=32, tm_wday=2, tm_yday=237, tm_isdst=-1)
>>>
6)time.asctime将struct_time转换成Wed Aug 24 14:22:47 2016这种格式
>>> x
time.struct_time(tm_year=2016, tm_mon=8, tm_mday=24, tm_hour=14, tm_min=22, tm_sec=47, tm_wday=2, tm_yday=237, tm_isdst=0)
>>> time.asctime(x)
'Wed Aug 24 14:22:47 2016'
>>>
7)time.ctime将时间戳装换成Wed Aug 24 14:22:47 2016格式
>>> x = time.time()
>>> x
1472019984.958831
>>> time.ctime(x)
'Wed Aug 24 14:26:24 2016'
1 %a 本地(locale)简化星期名称 2 %A 本地完整星期名称 3 %b 本地简化月份名称 4 %B 本地完整月份名称 5 %c 本地相应的日期和时间表示 6 %d 一个月中的第几天(01 - 31) 7 %H 一天中的第几个小时(24小时制,00 - 23) 8 %I 第几个小时(12小时制,01 - 12) 9 %j 一年中的第几天(001 - 366) 10 %m 月份(01 - 12) 11 %M 分钟数(00 - 59) 12 %p 本地am或者pm的相应符 13 %S 秒(01 - 61) 14 %U 一年中的星期数。(00 - 53星期天是一个星期的开始。)第一个星期天之前的所有天数都放在第0周。 15 %w 一个星期中的第几天(0 - 6,0是星期天) 16 %W 和%U基本相同,不同的是%W以星期一为一个星期的开始。 17 %x 本地相应日期 18 %X 本地相应时间 19 %y 去掉世纪的年份(00 - 99) 20 %Y 完整的年份 21 %Z 时区的名字(如果不存在为空字符) 22 %% ‘%’字符
datetime
当前时间
datetime.datetime.now()
时间替换
# c_time = datetime.datetime.now()
# print(c_time.replace(minute=3,hour=2))
时间加减
1 import datetime 2 3 # print(datetime.datetime.now()) #返回 2016-08-19 12:47:03.941925 4 #print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式 2016-08-19 5 # print(datetime.datetime.now() ) 6 # print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天 7 # print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 8 # print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时 9 # print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分
三、random随机模块 |
random.randint(1,3)则可以取出随机1-3
random.randrange(1,3)随机从范围内所及,不包括3
random.choice()传递的参数是序列包括字符串列表等
>>> random.choice("hello") 'l' >>> random.choice("hello") 'o' >>> random.choice("hello") 'e' >>> >>> random.choice(["我","爱","你"]) '我' >>> random.choice(["我","爱","你"]) '你' >>> random.choice(["我","爱","你"]) '你' >>> random.choice(["我","爱","你"]) '爱' >>>
random.sample()随机从前面的序列取出两位
1 >>> random.sample("hello",2) 2 3 ['l', 'o'] 4 5 >>> random.sample("hello",2) 6 7 ['h', 'l'] 8 9 >>> random.sample("hello",2) 10 11 ['h', 'o'] 12 13 >>>
random.shuffle 洗牌功能
1 >>> a=[1,2,3,4,5,6,7,8,9] 2 3 >>> random.shuffle(a) 4 5 >>> a 6 7 [6, 3, 7, 4, 1, 8, 9, 2, 5] 8 9 >>>
生成随机验证码的例子:
1 import string 2 import random 3 a = "".join(random.sample(string.ascii_lowercase,4)) 4 print(a) 5 b = "".join(random.sample(string.ascii_lowercase+string.digits,5)) 6 print(b) 7 8 c = "".join(random.sample(string.ascii_uppercase+string.digits+string.ascii_lowercase,4)) 9 print(c) 10 d ="".join(random.sample(string.ascii_letters+string.digits,4)) 11 print(d) 12 #结果如下 13 tbdy 14 6te4b 15 Z2UA 16 v8He 17 ----------------------------------------- 18 import random 19 checkcode = '' 20 for i in range(4): 21 current = random.randrange(0,4) 22 if current != i: 23 temp = chr(random.randint(65,90)) 24 else: 25 temp = random.randint(0,9) 26 checkcode += str(temp) 27 print checkcode
四、OS模块 |
提供对操作系统进行调用的接口
1 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 2 os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd 3 os.curdir 返回当前目录: ('.') 4 os.pardir 获取当前目录的父目录字符串名:('..') 5 os.makedirs('dirname1/dirname2') 可生成多层递归目录 6 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 7 os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname 8 os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname 9 os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 10 os.remove() 删除一个文件 11 os.rename("oldname","newname") 重命名文件/目录 12 os.stat('path/filename') 获取文件/目录信息 13 os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" 14 os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" 15 os.pathsep 输出用于分割文件路径的字符串 16 os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix' 17 os.system("bash command") 运行shell命令,直接显示 18 os.environ 获取系统环境变量 19 os.path.abspath(path) 返回path规范化的绝对路径 20 os.path.split(path) 将path分割成目录和文件名二元组返回 21 os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素 22 os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素 23 os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False 24 os.path.isabs(path) 如果path是绝对路径,返回True 25 os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False 26 os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False 27 os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 28 os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间
五、sys模块 |
1 sys.argv 命令行参数List,第一个元素是程序本身路径 2 sys.exit(n) 退出程序,正常退出时exit(0) 3 sys.version 获取Python解释程序的版本信息 4 sys.maxint 最大的Int值 5 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 6 sys.platform 返回操作系统平台名称 7 sys.stdout.write('please:') 8 val = sys.stdin.readline()[:-1]
文件copy小程序
#copy 文件 from sys import argv #传递多个参数 from os.path import exists from os import chdir from os import getcwd chdir("d:\\桌面") #改变工作目录 my_dir=getcwd()+"\\"#获得当前工作目录 script,from_file,to_file=argv#按顺序指定参数 print("你需要复制{}并命名为{}吗?".format(from_file,to_file)) with open(my_dir+from_file) as f: indata=f.read() print("文件大小{}bytes".format(len(my_dir+from_file))) print("文件是否存在?{}".format(exists(my_dir+to_file))) input("enter继续,ctrl-c结束") with open(my_dir+to_file,"w") as f: f.write(indata) print("复制完成") ------------------------------------------------- 运行 python copy.py test1.txt test2.txt 结果: 你需要复制test1.txt并命名为test2.txt吗? 文件大小15bytes 文件是否存在?False enter继续,ctrl-c结束 复制完成
六、shutil 模块 |
高级的 文件、文件夹、压缩包 处理模块
import shutil a = open("a.txt","r",encoding="utf-8") b = open("b.txt","w",encoding="utf-8") shutil.copyfileobj(a,b)
运行够会复制一个文件b,将a文件中的内容复制到b文件中
shutil.copyfile("b.txt","c.txt")直接复制b.txt到c.txt
shutil.copymode(src,dst) 仅拷贝权限。内容,组,用户均不变
shutil.copystat(src,dst)拷贝状态的信息
shutil.copytree(src,dst,symlinks=false,ignore=none) 递归拷贝文件
shutil.rmtree(path[,ignore_errors[,onerror]])
shutil.move(sr,dst):递归移动文件
shutil.make_archive(base_name, format,...)#创建压缩包
#将 /Users/wupeiqi/Downloads/test 下的文件打包放置当前程序目录 import shutil ret = shutil.make_archive("wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test') #将 /Users/wupeiqi/Downloads/test 下的文件打包放置 /Users/wupeiqi/目录 import shutil ret = shutil.make_archive("/Users/wupeiqi/wwwwwwwwww", 'gztar', root_dir='/Users/wupeiqi/Downloads/test')
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() z.close() import tarfile # 压缩 tar = tarfile.open('your.tar','w') tar.add('/Users/wupeiqi/PycharmProjects/bbs2.zip', arcname='bbs2.zip') tar.add('/Users/wupeiqi/PycharmProjects/cmdb.zip', arcname='cmdb.zip') tar.close() # 解压 tar = tarfile.open('your.tar','r') tar.extractall() # 可设置解压地址 tar.close()
七、json & picle序列化模块 |
用于序列化的两个模块
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
Json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
1 data={"name":"susu","age":25} 2 #json序列化 3 import json 4 data={"name":"susu","age":25} 5 f=open("test.txt","w") 6 f.write(json.dumps(data)) 7 #json反序列化 8 import json 9 f=open("test.txt","r") 10 data=json.loads(f.read()) 11 f.close()
pickle可以处理更复杂的数据,用法和json一样。以二进制写入读取
f.write(pickle.dumps(data))和pickle.dump(data,f)
pickle.loads(f.read()和pickle.load(f)
基本一样
不同在于,pickle是二进制写入读取
注:python3 dump 一次,load一次,如果想要dump多次呢,就用shelve
八、shelve 模块 |
shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式
import shelve d = shelve .open('shelve_test') # 打开一个文件 def stu_data(name,age): print("register",name,age) name=["susu","rain","test"] d["test"] = name # 持久化列表 d["func"]=stu_data d.close() 读取: f = shelve .open('shelve_test') print(f["test"]) print(f["fun"])
print(type(f)) print("test" in f) 结果: <class 'shelve.DbfilenameShelf'> True
九、xml处理 |
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
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协议在各个语言里的都 是支持的,在python中可以用以下模块操作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) for i in child: print(i.tag,i.text) #只遍历year 节点 for node in root.iter('year'): print(node.tag,node.text)
修改和删除xml文档内容
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") tree.write("xmltest.xml") #删除node for country in root.findall('country'): rank = int(country.find('rank').text) if rank > 50: root.remove(country) tree.write('output.xml')
自己创建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) #打印生成的格式
十、ConfigParser模块 |
用于生成和修改常见配置文档,当前模块的名称在 python 3.x 版本中变更为 configparser。
来看一个好多软件的常见文档格式如下
[DEFAULT] ServerAliveInterval = 45 Compression = yes CompressionLevel = 9 ForwardX11 = yes [bitbucket.org] User = hg [topsecret.server.com] Port = 50022 ForwardX11 = no
如果想用python生成一个这样的文档怎么做呢?
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)
写完了还可以再读出来哈。
>>> import configparser >>> config = configparser.ConfigParser() >>> config.sections() [] >>> config.read('example.ini') ['example.ini'] >>> config.sections() ['bitbucket.org', 'topsecret.server.com'] >>> 'bitbucket.org' in config True >>> 'bytebong.com' in config False >>> config['bitbucket.org']['User'] 'hg' >>> config['DEFAULT']['Compression'] 'yes' >>> topsecret = config['topsecret.server.com'] >>> topsecret['ForwardX11'] 'no' >>> topsecret['Port'] '50022' >>> for key in config['bitbucket.org']: print(key) ... user compressionlevel serveraliveinterval compression forwardx11 >>> config['bitbucket.org']['ForwardX11'] 'yes'
configparser增删改查语法
[section1] k1 = v1 k2:v2 [section2] k1 = v1 import ConfigParser config = ConfigParser.ConfigParser() config.read('i.cfg') # ########## 读 ########## #secs = config.sections() #print secs #options = config.options('group2') #print options #item_list = config.items('group2') #print item_list #val = config.get('group1','key') #val = config.getint('group1','key') # ########## 改写 ########## #sec = config.remove_section('group1') #config.write(open('i.cfg', "w")) #sec = config.has_section('wupeiqi') #sec = config.add_section('wupeiqi') #config.write(open('i.cfg', "w")) #config.set('group2','k1',11111) #config.write(open('i.cfg', "w")) #config.remove_option('group2','age') #config.write(open('i.cfg', "w"))
十一、hashlib模块 |
1、MD5加密
原则:只要你的输入是固定的,你的输出也一定是固定的。MD5是在hash上更改的,主要做文件的一致性
1 import hashlib 2 3 m = hashlib.md5() #创建一个MD5对象 4 m.update(b"zhang") #在python3中需要是2进制的值,所以字符串前加b 5 print(m.hexdigest()) #以16进制打印MD5值 6 #输出 7 d0cd2693b3506677e4c55e91d6365bff 8 9 m.update(b"qigao") 10 print(m.hexdigest()) 11 #输出 12 0bfca190ecc60e44cbc739ca9c252133
2、文件内容MD5值说明:如果我们想得到一个文件所有内容的MD5值,我们所做的方法是循环这个文件,获取每行的MD5值,但是这样生成的MD5值的效率会变慢,因为每一行都需要计算。这样我们还不如直接把文件的所有内容加载出来,直接计算它的MD5值,这样反而快些。
代码如下:
1 import hashlib 2 3 m = hashlib.md5() #创建MD5对象m 4 m.update(b"zhang") 5 print(m.hexdigest()) 6 #输出 7 d0cd2693b3506677e4c55e91d6365bff 8 9 m.update(b"qigao") 10 print(m.hexdigest()) 11 #输出 12 0bfca190ecc60e44cbc739ca9c252133 13 14 m2 = hashlib.md5() #创建MD5对象m2 15 m2.update(b"zhangqigao") 16 print(m2.hexdigest()) 17 #输出 18 0bfca190ecc60e44cbc739ca9c252133
注:由上面的代码可以看出,你读到最后一行的字符串的MD5值跟一下子读取所有内容的MD5值是一样的,这是为什么呢?其实这边update做了一个拼接功能,m.update(b"zhang")是返回的字符串"zhang"的MD5值,但是到了第二个m.update("qigao")的值并不是"qigao"的字符串的MD5值,它需要拼接前面的字符串,应该是m.update(b"zhangqigao")的MD5值,所以相当于m.update(b"zhang"),m.update(b"qigao") = m.update(b"zhang"+b"qigao")。
3、sha1加密、sha256加密、sha384加密、sha512加密
其实都是对MD5加密的不同算法,其中sha256用的最多,比MD5要安全的多
有些公司会用加盐方式加密,比如:把字符串"zhangqigao",通过一定的算法变成"zhang.qi.gao",当然这种算法自己肯定要知道,然后MD5加密,当然每个公司的加盐方式是不一样的。
1 import hashlib 2 3 hash = hashlib.sha1() 4 hash.update(b"zhangqigao") 5 print(hash.hexdigest()) 6 7 #输出 8 c8b2a6571067f92133b5b43a085f1ddd36e8c3fb 9 10 11 hash = hashlib.sha256() 12 hash.update(b"zhangqigao") 13 print(hash.hexdigest()) 14 15 #输出 16 0634de5fe3d009fd0ec76ab3d97ab0fe37969b696e8d6550797cf3b446dd78ba 17 18 hash = hashlib.sha384() 19 hash.update(b"zhangqigao") 20 print(hash.hexdigest()) 21 22 #输出 23 3489c64e31671094ca1afde35fd31ee9b09cdb90c3728f31696829e8a56be311e1405d537179e62d236e6d70a4f13ff4 24 25 hash = hashlib.sha512() 26 hash.update(b"zhangqigao") 27 print(hash.hexdigest()) 28 29 #输出 30 cb09fd5a519b2b075f4aa5965a39657df900fff832b73d161a426512b6023ab8c1c0872a7b2d50055cbd75c4b6f374cda0615be9530f7f4b7dc08ab3f266325d
4、hmac加密
其实以上还不是最牛的,最牛的是下面这种,叫hmac加密,它内部是对我们创建key和内容进行处理再进行加密。
散列消息鉴别码,简称HMAC,是一种基于消息鉴别码MAC(Message Authentication Code)的鉴别机制。使用HMAC时,消息通讯的双方,通过验证消息中加入的鉴别密钥K来鉴别消息的真伪;
一般用于网络通信中消息加密,前提是双方先要约定好key,就像接头暗号一样,然后消息发送把用key把消息加密,接收方用key + 消息明文再加密,拿加密后的值 跟 发送者的相对比是否相等,这样就能验证消息的真实性,及发送者的合法性了。
代码如下:
1 import hmac #导入hmac模块 2 3 hash = hmac.new(b"zhang",b"qigao") #zhang是key,qigao是内容 4 print(hash.hexdigest()) 5 6 #输出 7 2f124c86aeb5142246198f77a142e855
注:本模块介绍转自张同学博客:http://www.cnblogs.com/zhangqigao/articles/6689354.html,谢谢他的细心总结。
十二、logging模块 |
logging的日志可以分为debug,info,warning,error和critical 5个级别,下面我们就来看看这个日志模块logging怎么用
1.日志级别
Level | When it’s used |
---|---|
DEBUG |
Detailed information, typically of interest only when diagnosing problems. |
INFO |
Confirmation that things are working as expected. |
WARNING |
An indication that something unexpected happened, or indicative of some problem in the near future (e.g. ‘disk space low’). The software is still working as expected. |
ERROR |
Due to a more serious problem, the software has not been able to perform some function. |
CRITICAL |
A serious error, indicating that the program itself may be unable to continue running. |
2.日志写入文件
1 import logging 2 3 logging.basicConfig(filename="catalina.log",level=logging.INFO) #输入文件名,和日志级别 4 #---日志输出--- 5 logging.debug("logging debug") 6 logging.info("logging info") 7 logging.warning("logging warning") 8 9 #输出到文件中的内容 10 INFO:root:logging info 11 WARNING:root:logging warning
注: 这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,所以debug日志没有记录,如果想记录,则级别设置成debug也就是level=loggin.DEBUG
3.加入日期格式
说明:感觉上面的日志格式忘记加上时间啦,日志不知道时间怎么行呢,下面就来加上
1 logging.basicConfig(filename="catalina.log", 2 level=logging.INFO, 3 format='%(asctime)s %(module)s:%(levelname)s %(message)s', #格式请见第5点内容 4 datefmt='%m/%d/%Y %H:%M:%S %p') #需要加上format和datefmt 5 6 #----日志内容----- 7 logging.debug("logging debug") 8 logging.info("logging info") 9 logging.warning("logging warning") 10 11 #文件输出 12 04/11/2017 14:20:22 PM logging_mod:INFO logging info 13 04/11/2017 14:20:22 PM logging_mod:WARNING logging warning
4.format的日志格式
%(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 |
用户输出的消息 |
logging.basicConfig(format='%(asctime)s%(message)s:%(lineno)d',datefmt='%m/%d/%Y %I:%M:%S %p') logging.critical('And this, too') 结果: 05/22/2017 02:03:16 PMAnd this, too:3
5.复杂日志输出
如果想同时把log打印在屏幕和文件日志里,就需要了解一点复杂的知识 了
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():可以设置的日志级别
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并不会被更名,而是被删除。
import logging from logging import handlers #需要导入handlers logger = logging.getLogger(__name__) log_file = "timelog.log" #按文件大小来分割,10个字节,保留个数是3个 fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test1") logger.warning("test12") logger.warning("test13") logger.warning("test14")
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 每天凌晨
import logging from logging import handlers #需要导入handlers logger = logging.getLogger(__name__) log_file = "timelog.log" #按文件大小来分割,10个字节,保留个数是3个 fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test1") logger.warning("test12") logger.warning("test13") logger.warning("test14")
import logging #create logger logger = logging.getLogger('TEST-LOG') logger.setLevel(logging.DEBUG) # create console handler and set level to debug ch = logging.StreamHandler() ch.setLevel(logging.DEBUG) # create file handler and set level to warning fh = logging.FileHandler("access.log") fh.setLevel(logging.WARNING) # create formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # add formatter to ch and fh ch.setFormatter(formatter) fh.setFormatter(formatter) # add ch and fh to logger logger.addHandler(ch) logger.addHandler(fh) # 'application' code logger.debug('debug message') logger.info('info message') logger.warn('warn message') logger.error('error message') logger.critical('critical message')
文件自动截断例子
import logging from logging import handlers logger = logging.getLogger(__name__) log_file = "timelog.log" #fh = handlers.RotatingFileHandler(filename=log_file,maxBytes=10,backupCount=3) fh = handlers.TimedRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3) formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.warning("test1") logger.warning("test12") logger.warning("test13") logger.warning("test14")
6、 控制台和文件日志共同输出
需要什么样的输出,只需要添加相应的handler就ok了。
1、逻辑图
1 import logging 2 3 #create logging 4 logger = logging.getLogger("TEST-LOG") 5 logger.setLevel(logging.DEBUG) 6 7 #屏幕handler 8 ch = logging.StreamHandler() 9 ch.setLevel(logging.DEBUG) 10 11 #文件handler 12 fh = logging.FileHandler("debug.log",encoding="utf-8") 13 fh.setLevel(logging.INFO) 14 #分别创建输出日志格式 15 ch_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") 16 fh_formatter = logging.Formatter("%(asctime)s %(module)s:%(levelname)s %(message)s") 17 #设置handler的输出格式 18 ch.setFormatter(ch_formatter) 19 fh.setFormatter(fh_formatter) 20 #添加handler 21 logger.addHandler(ch) 22 logger.addHandler(fh) 23 24 # 'application' code 25 logger.debug('debug message') 26 logger.info('info message') 27 logger.warn('warn message') 28 logger.error('error message') 29 logger.critical('critical message')
注:如果添加时间分割或者文件大小分割,再添加handler即可。
十三、re模块 |
常用正则表达式符号
'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE) '$' 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以 '*' 匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'] '+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] '?' 匹配前一个字符1次或0次 '{m}' 匹配前一个字符m次 '{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] '|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' '(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c '\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的 '\Z' 匹配字符结尾,同$ '\d' 匹配数字0-9 '\D' 匹配非数字 '\w' 匹配[A-Za-z0-9] '\W' 匹配非[A-Za-z0-9] 's' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' '(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}
最常用的匹配语法
1 re.match 从头开始匹配 2 re.search 匹配包含 只匹配一次 3 re.findall 把所有匹配到的字符放到以列表中的元素返回 4 re.split 以匹配到的字符当做列表分隔符 5 re.sub 匹配字符并替换>>> re.sub(
'\.'
,
'-'
,
"inet addri:10.161.146.134"
)
'inet addri:10-161-146-134'
#默认全部替换
>>> re.sub(
'\.'
,
'-'
,
"inet addri:10.161.146.134"
,count
=
2
)
'inet addri:10-161-146.134'
#用count控制替换次数
常用用法
1、group([group1, ...])
>>> import re >>> a = re.search('addr',"inet addr:10.161.146.134") >>> a.group() 'addr' >>> a.group(0) 'addr'
2、groups(default=None)
默认为None。这个要跟分组匹配结合起来使用'(...)'
>>> import re >>> a = re.search("(\d{2})(\d{2})(\d{2})(\d{4})","320922199508083319") #一个小括号表示一个组,有4个括号,就是4个组 >>> a.groups() ('32', '09', '22', '1995')
3、groupdict(default=None)
>>> import re >>> a = re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242") #以下两种情况获取的值都是一样的 >>> a.groupdict() {'birthday': '1993', 'city': '81', 'province': '3714'} >>> a.groupdict("city") {'birthday': '1993', 'city': '81', 'province': '3714'}
4、span([group]) start([group])
>>> import re >>> a = re.search('addr',"inet addr:10.161.146.134") >>> a.group() 'addr' >>> a.span() #获取'addr'在字符串中的开始位置和结束位置 (5, 9) >>> a.start() #获取字符串的起始索引 5
5、end([group])
说明:返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1),group默认值为0
>>> import re >>> a = re.search('addr',"inet addr:10.161.146.134") >>> a.group() 'addr' >>> a.span() (5, 9) >>> a.end() #获取string中的结束索引 9
6、compile(pattern[, flags])
>>> import re >>> m = re.compile("addr") #创建正则模式对象 >>> n = m.search("inet addr:10.161.146.134") #通过模式对象去匹配 >>> n.group() 'addr'
反斜杠的困扰('\\\\'or r'\\')
与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
>>> import re >>> a = re.split("\\\\","C:\ \zhangqigao\yhd_settings") >>> a ['C:', ' ', 'zhangqigao', 'yhd_settings'] >>> re.findall('\\','abc\com') Traceback (most recent call last) >>> re.findall('\\\\','abc\com') ['\\'] >>> re.findall(r'\\','abc\com') ['\\']
仅需轻轻知道的几个匹配模式
1、re.I(re.IGNORECASE
)
说明:忽略大小写(括号内是完整的写法,下同)
>>> import re >>> a = re.search('addr',"inet Addr:10.161.146.134",flags=re.I) >>> a.group() 'Addr' #忽略大小写
2、re.M(MULTILINE
)
说明:多行模式,改变'^'和'$'的行为
>>> import re >>> a = re.search('^a',"inet\naddr:10.161.146.134",flags=re.M) >>> a.group() 'a'
3、re.S(DOTALL
)
说明:点任意匹配模式,改变'.'的行为
>>> import re >>> a = re.search('.+',"inet\naddr:10.161.146.134",flags=re.S) >>> a.group() 'inet\naddr:10.161.146.134'
案列
匹配出ansible操作日志 2018-08-21 17:03:02,397 p=8980 u=root | host1 | SUCCESS => { "changed": false, "ping": "pong" } 2018-08-21 17:03:11,369 p=9090 u=root | host1 | SUCCESS => { "changed": false, "ping": "pong" } ------------------------------------------------------------------------------------ import json import re logfile="ansible.log" r = re.compile(r'(?P<time>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}).*\su=(?P<user>.*)\s\|\s\s(?P<host>.*)\s\|\s(?P<status>.*)\s=>.*') for line in open(logfile).readlines(): result=re.finditer(r,line) items = [m.groupdict() for m in result] if not items: continue data=(json.dumps(items[0])).encode('utf-8') print(data)
作业
开发一个简单的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等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
hint:
re.search(r'\([^()]+\)',s).group()
'(-40/5)'