Python的常用模块
python的常用模块
一、time模块
# time模块
# python中时间分为三种格式:
# 1、时间戳(timestamp):从1970年到现在经过的秒数
# 作用:用于时间间隔的计算
import time
print(time.time())
# 2、按某种格式显示的时间(Format String):2020-04-15 09:09:09
# 作用:用于展示时间
print(time.strftime('%Y-%m-%d %H:%M:%S %p')) # 2020-04-15 09:20:27 AM
print(time.strftime('%Y-%m-%d %X')) # 2020-04-15 09:20:48
print(time.strftime('%Y-%m-%d %Y')) # 2020-04-15 2020
# 3、结构化的时间(struct_time)
# 作用:用于单独获取时间的某一部分
res = time.localtime()
print(res)
"""运行结果:
time.struct_time(tm_year=2020, tm_mon=4, tm_mday=15, tm_hour=9,
tm_min=27, tm_sec=6, tm_wday=2, tm_yday=106, tm_isdst=0)
tm_year=2020 当前年
tm_mon=4 当前月
tm_mday=15 当前天
tm_hour=9 当前小时
tm_min=27 当前分钟
tm_sec=6 当前秒
tm_wday=2 当前是周几,从0开始:0代表星期一
tm_yday=106 当前所在年的天:当前年的第106天。
tm_isdst=0 是否是夏令时
"""
print(res.tm_year) # 获取当前年-->2020
print(res.tm_yday) # 当前所在年的天-->106
%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.
二、datetime模块
import datetime
# 获取当前时间
print(datetime.datetime.now()) # 2020-04-15 09:41:48.551997
# 在此时间基础上参与时间的运算:当前时间+3天后的时间。3可以是正数也可以是负数。
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 2020-04-18 09:47:26.094479
"""timedelta(days=)语法
def __init__(self, days: float = ..., seconds: float = ..., microseconds: float = ...,
milliseconds: float = ..., minutes: float = ..., hours: float = ...,
weeks: float = ...) -> None: ...
"""
三、时间格式的转换
计算机认识的时间只能是“时间戳”格式。
人类能读懂的时间有:格式化的字符串时间 ,机构化的时间
三种转换关系如下图所示
import time
# 时间模块需要掌握的操作
# 1、时间格式的转换
# 结构化时间struct_time --> 时间戳Timestamp
# s_time = time.localtime()
# print(time.mktime(s_time)) # 1586916748.0
# print(time.time()) # 1586916748.0209408
# 时间戳Timestamp --> 结构化时间:struct_time
# tp_time = time.time()
# print(time.localtime(tp_time))
# time.struct_time(tm_year=2020, tm_mon=4, tm_mday=15, tm_hour=10, tm_min=13, tm_sec=38, tm_wday=2, tm_yday=106, tm_isdst=0)
# 结构化时间struct_time -->格式化的字符串形式的时间
s_time = time.localtime()
print(time.strftime('%Y-%m-%d %H:%M:%S',s_time)) # 2020-04-15 10:27:20
# 格式化的字符串时间-->结构化时间struct_time
print(time.strptime('2020-04-15 10:27:20','%Y-%m-%d %H:%M:%S'))
# time.struct_time(tm_year=2020, tm_mon=4, tm_mday=15, tm_hour=10, tm_min=27, tm_sec=20, tm_wday=2, tm_yday=106, tm_isdst=-1)
# 重点掌握:格式化的字符串时间format string <--> 时间戳timestamp,互相转换,通过结构化中转
# 假设需求:格式化字符串时间是2020-04-15 10:27:20,该时间是VIP注册的时间,用户又充值7天时间
# 1、先将格式化字符串时间转换成结构化时间(forma string --> struct_time)
struct_time = time.strptime('2020-04-15 10:27:20','%Y-%m-%d %H:%M:%S')
# 2、将结构化时间转换成时间戳(struct_time-->timestamp,一天是时间是=24*60*60=86400秒),加7天时间
timestamp = time.mktime(struct_time) + 7*86400
print(timestamp) # 1587522440.0
# 3、将时间戳转换成结构化时间(timestamp-->struct_time)
struct_time_1 = time.localtime(timestamp)
# 4、将结构化时间转换成格式化字符串时间(struct_time --> Format String)
format_string = time.strftime('%Y-%m-%d %H:%M:%S',struct_time_1)
print(format_string) # 2020-04-22 10:27:20
# 直接将时间戳直接转换成格式化字符串的时间
print(datetime.datetime.fromtimestamp(333333)) # 1970-01-05 04:35:33
# 补充:世界标准时间与本地时间
# print(time.localtime()) # 本地时间,tm_hour本地时间与世界标准时间相差8小时
# print(time.gmtime()) # 世界标准时间 # tm_hour=2世界标准时间
# print(time.localtime(333333333)) # tm_hour=8
# print(time.gmtime(333333333)) # tm_hour=0
# import time
# 了解知识
# 休眠
# time.sleep(3)
# time.strftime()与time.asctime()区别,不用%
# print(time.asctime()) # Wed Apr 15 12:39:50 2020
# print(time.strftime())
import datetime
# 获取当前时间
print(datetime.datetime.now()) # 2020-04-15 12:43:04.573067
print(datetime.datetime.utcnow()) # 2020-04-15 04:43:04.573067
四、random模块
1 import random
2
3 print(random.random())#(0,1)----float 大于0且小于1之间的小数
4
5 print(random.randint(1,3)) #[1,3] 大于等于1且小于等于3之间的整数
6
7 print(random.randrange(1,3)) #[1,3) 大于等于1且小于3之间的整数
8
9 print(random.choice([1,'23',[4,5]]))#1或者23或者[4,5]
10
11 print(random.sample([1,'23',[4,5]],2))#列表元素任意2个组合
12
13 print(random.uniform(1,3))#大于1小于3的小数,如1.927109612082716
14
15
16 item=[1,3,5,7,9]
17 random.shuffle(item) #打乱item的顺序,相当于"洗牌" ,应用:随机验证码
18 print(item)
# 随机验证码实例
import random
res=''
for i in range(6):
# 获取随机字母或数字
# 随机字符=random.choice([从26个大写英文字母中随机取出一个,从10个数字中随机取出一个])
# 从26个大写英文字母中随机取出一个=chr(random.randint(65,90))
# 从10个数字中随机取出一个=random.rangdint(0,9),得到的是一个整型 str转换成字符串str(random.rangdint(0,9))
# res += 随机字符
# 实现如下:
s1 = chr(random.randint(65,90))
s2 = str(random.randint(0,9))
res += random.choice([s1,s2])
print(res)
# 随机验证码实例
import random
res=''
for i in range(6):
s1 = chr(random.randint(65,90))
s2 = str(random.randint(0,9))
res += random.choice([s1,s2])
print(res)
# 思考:有的验证码是4位,有的验证码是6位,所以参数位置不能写死,修改如下:
def make_code(size=4):
res = ''
for i in range(size):
s1 = chr(random.randint(65, 90))
s2 = str(random.randint(0, 9))
res += random.choice([s1, s2])
print(res)
print(make_code()) # 不传参就是默认4位
# print(make_code(6)) # 传个6位的参数,就是6位
# 查看ASCII表中对应的数值
print(chr(65)) # A
print(chr(90)) # Z
print(ord('A')) # 65
五、os模块
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd
os.curdir 返回当前目录: ('.')
os.pardir 获取当前目录的父目录字符串名:('..')
os.makedirs('dirname1/dirname2') 可生成多层递归目录
os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname
os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove() 删除一个文件
os.rename("oldname","newname") 重命名文件/目录
os.stat('path/filename') 获取文件/目录信息
os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
os.system("bash command") 运行shell命令,直接显示
os.environ 获取系统环境变量
os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) 如果path是绝对路径,返回True
os.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小
import os
# 查看某一个文件夹下有哪些子文件
# res = os.listdir(r'D:\pycharm\oldboy_29\day022\test022')
# print(res)
# 查看当前文件夹下所有的子文件以及子文件夹的名字
# res = os.listdir('.')
# print(res)
# 需求:统计某一个文件夹大小的功能,如果查看的是文件统计大小,如果是文件夹需要再次打开
# 获取文件大小 单位字节
# size = os.path.getsize(r'D:\pycharm\oldboy_29\day022\test022')
# print(size)
# 运行一条系统命令,
# dir:windows下查看
# os.system("dir C:\\a")
# 应用程序--->给操作系统发一条命令ls
# os.system('dir /')
# environ环境变量
# PATH = 文件夹
# sys.path # 导入模块
# 获取环境变量
# print(os.environ) # 运行后都是字典形式:{'ALLUSERSPROFILE': 'C:\\ProgramData'
# key与value必须都为字符串
# 加入一组key:value,我们后期在系统内可以用到的环境变量
# os.environ['aaaaaaaaaaaaaaaaaaaaaa'] = '1111111111111111111111'
# print(os.environ) # 运行如下:...'AAAAAAAAAAAAAAAAAAAAAA': '1111111111111111111111'
os实例
import os
# print(__file__) # 查看文件所在位置:D:/pycharm/oldboy_29/day022/test022/test022_09ospath.py
# print(os.path.abspath(__file__)) # 根据平台显示对应的路径分割符格式:D:\pycharm\oldboy_29\day022\test022\test022_09ospath.py
# res = os.path.split('/a/b/c/d.txt')
# print(res) # 以元组形式划分文件夹名称和文件名称('/a/b/c', 'd.txt')
# 获取文件夹名称
# print(os.path.dirname(r'/a/b/c/d.txt')) # /a/b/c
# 获取文件名称
# print(os.path.basename(r'/a/b/c/d.txt')) # d.txt
# 判断是否是绝对路径
# print(os.path.isabs('/a/b/c')) # 是返回True
# print(os.path.isabs('D:\Python38\python.exe')) # 是返回True
# 判断存在的文件
# print(os.path.isfile(r'test022_09ospath.py')) # True
# print(os.path.isfile(r'test022_09ospath1.py')) # False
# print(os.path.isfile(r'test022')) # 这是个文件夹所以返回False
# 判断是否是文件夹
print(os.path.isdir(r'day022')) # True
# 将多个路径组合后返回
print(os.path.join('a','/','b'))
在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。
>>> os.path.normcase('c:/windows\\system32\\')
'c:\\windows\\system32\\'
规范化路径,如..和/ 一个..就向上跳一级
>>> 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
# 在python3.5后,退出一个新的模块pathlib
from pathlib import Path
root = Path(__file__)
res = root.parent.parent
print(res)
# D:\pycharm\oldboy_29\day022
六、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 返回操作系统平台名称
import sys
# sys.argv获取的是解释器后参数值
print(sys.argv)
# 在交换模式下 python test022_11.py 1 2 3
# D:\pycharm\oldboy_29\day022\test022>python test022_11.py 1 2 3
# ['test022_11.py', '1', '2', '3']
#=========知识储备==========
#进度条的效果
# [# ]
# [## ]
# [### ]
# [#### ]
#指定宽度
# print('[%-15s]' %'#')
# print('[%-15s]' %'##')
# print('[%-15s]' %'###')
# print('[%-15s]' %'####')
import time
# res = ''
# for i in range(50):
# res+="#"
# time.sleep(0.3)
# print('\r[%-50s]' % res,end='')
import time
def progress(percent):
# 进度条的功能
if percent > 1:
percent = 1
res = int(50*percent) * '#'
print('\r[%-50s] %d%%' % (res,int(percent*100)), end='')
recv_size = 0
total_size = 33333333333
while recv_size < total_size:
time.sleep(0.001) # 下载了1024个字节的数据
recv_size += 1024
# 打印进度条
# print(recv_size)
percent = recv_size / total_size # 1024 / 3333333
progress(percent)
七、shutil模块
高级的 文件、文件夹、压缩包 处理模块
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中
1 import shutil
2
3 shutil.copyfileobj(open('old.xml','r'), open('new.xml', '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的意思是排除
import shutil
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 两个模块来进行的,详细:
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模块
之前我们学过用eval内置方法,可以将一个字符串转成python对象,不过,eval方法是有局限性的,对于普通的数据类型,json.loads和eval都能用,但遇到特殊类型的时候,eval就不管用了,所以eval重点还是通常用来执行一个字符串表达式,并返回表达式的值
# 1、什么是序列化&反序列化?
# 序列化指的是把内存的数据类型转换成一种特定的格式的内容,
#
# 内存中的数据类型-->序列化--> 特定的格式(json格式或者pickle格式)
# 内存中的数据类型<--反序列化<--特定的格式(json格式或者pickle格式)
#
# 土办法:
# {'a':1} -->序列化str({'a':1})-->"{{'a':1}}"
# {'a':1} <--反序列化eval({'a':1})<--"{{'a':1}}"
#
# 2、为何要序列化?
# 序列化得到的结果-->特定的格式的内容有两种用途。
# 1、可用于存储(变量的值可以记录某种状态)-->用于存档
# 2、传输给其他平台使用-->跨平台数据交互
#
# 例如:同一个软件内有不同的组件,不同的组件使用不用的语言实现。如何进行数据交互。
# Python的一个列表给了Java,到java中就是数组,在Python和java之间找个第三方中间格式
# Python java
# 列表 特殊格式 数组
# 强调:
# 针对用途1的特定格式:可以是一种专用的格式-->pickle只有python可以识别
# 针对用途2的特定格式:应该是一种通用、能够被所有语言识别的格式==>json
#
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下
# 3、如何使用序列化和反序列化
import json
# 示范一
# 序列化
# json_res = json.dumps(True)
# print(res,type(json_res)) # true <class 'str'>
# json_res = json.dumps([1,'a',True,False])
# print(res,type(json_res)) # 得到json格式:[1, "a", true, false] <class 'str'>
# 反序列化
# l = json.loads(json_res)
# print(l,type(l)) # 得到python格式的:[1, 'a', True, False] <class 'list'>
# 示范二:
# 序列化的结果写入文件的复杂方法,dumps
# json_res = json.dumps([1,'a',True,False])
# with open('test.json',mode='wt',encoding='utf-8') as f:
# f.write(json_res)
# # 运行结果:[1, "a", true, false]
# 将序列化的结果写入文件的简单方法:dump,一步到位
# with open('test.json',mode='wt',encoding='utf-8') as f:
# json.dump([1,'a',True,False],f)
# 运行结果:[1, "a", true, false]
# 反序列化:从文件读取json格式的字符串进行序列化操作的复杂方法。loads
# with open('test.json',mode='rt',encoding='utf-8') as f:
# json_res = f.read()
# l = json.loads(json_res)
# print(l,type(l))
# 运行结果:[1, 'a', True, False] <class 'list'>
# 反序列化:从文件读取json格式的字符串进行序列化操作的简单方法。load
# with open('test.json',mode='rt',encoding='utf-8') as f:
# l = json.load(f)
# print(l,type(l))
# 运行结果:[1, 'a', True, False] <class 'list'>
# json补充
# json验证:json格式兼容的是所有语言通用的数据类型,不能识别某一语言所独有的类型
import json
# json.dumps({1,2,3,4,5})
# json强调:json.loads('[1, "a", true, false]')格式要放json格式的内容
# l = json.loads('[1, "a", true, false]')
# print(l[0])
# 1
# json格式不能于python混淆,python格式里的True
# l = json.loads('[1, "a", True, False]')
# print(l[0])
# json与python格式混淆后报错,json.decoder.JSONDecodeError: Expecting value: line 1 column 10 (char 9)
# python3.5以前的版本要加入b
# l = json.loads(b'[1, "a", true, false]')
# print(l[0])
# with open('test.json',mode='rt') as f:
# l = json.load(f)
#
# res = json.dumps({'name':'hahaha哈哈哈'})
# print(res,type(res)) # {"name": "hahaha\u54c8\u54c8\u54c8"} <class 'str'>
# res = json.loads('{"name": "hahaha\u54c8\u54c8\u54c8"}')
# print(res,type(res)) # {'name': 'hahaha哈哈哈'} <class 'dict'>
九、猴子补丁
# 一.什么是猴子补丁?
猴子补丁的核心就是用自己的代码替换所用模块的源代码,详细地如下
1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。
2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。
# 二. 猴子补丁的功能(一切皆对象)
1.拥有在模块运行时替换的功能, 例如: 一个函数对象赋值给另外一个函数对象(把函数原本的执行的功能给替换了)
class Monkey:
def hello(self):
print('hello')
def world(self):
print('world')
def other_func():
print("from other_func")
monkey = Monkey()
monkey.hello = monkey.world
monkey.hello()
monkey.world = other_func
monkey.world()
# 三.monkey patch的应用场景
如果我们的程序中已经基于json模块编写了大量代码了,发现有一个模块ujson比它性能更高,
但用法一样,我们肯定不会想所有的代码都换成ujson.dumps或者ujson.loads,那我们可能
会想到这么做
import ujson as json,但是这么做的需要每个文件都重新导入一下,维护成本依然很高
此时我们就可以用到猴子补丁了
只需要在入口处加上
, 只需要在入口加上:
import json
import ujson
def monkey_patch_json():
json.__name__ = 'ujson'
json.dumps = ujson.dumps
json.loads = ujson.loads
monkey_patch_json() # 之所以在入口处加,是因为模块在导入一次后,后续的导入便直接引用第一次的成果
#其实这种场景也比较多, 比如我们引用团队通用库里的一个模块, 又想丰富模块的功能, 除了继承之外也可以考虑用Monkey
Patch.采用猴子补丁之后,如果发现ujson不符合预期,那也可以快速撤掉补丁。个人感觉Monkey
Patch带了便利的同时也有搞乱源代码的风险!
猴子补丁与ujson
十、pickle模块
import pickle
dic={'name':'alvin','age':23,'sex':'male'}
print(type(dic))#<class 'dict'>
j=pickle.dumps(dic)
print(type(j))#<class 'bytes'>
f=open('序列化对象_pickle','wb')#注意是w是写入str,wb是写入bytes,j是'bytes'
f.write(j) #-------------------等价于pickle.dump(dic,f)
f.close()
#-------------------------反序列化
import pickle
f=open('序列化对象_pickle','rb')
data=pickle.loads(f.read())# 等价于data=pickle.load(f)
print(data['age'])
# coding:utf-8
import pickle
with open('a.pkl',mode='wb') as f:
# 一:在python3中执行的序列化操作如何兼容python2
# python2不支持protocol>2,默认python3中protocol=4
# 所以在python3中dump操作应该指定protocol=2
pickle.dump('你好啊',f,protocol=2)
with open('a.pkl', mode='rb') as f:
# 二:python2中反序列化才能正常使用
res=pickle.load(f)
print(res)
python2与python3的pickle兼容性问题
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关系。
十一、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数据
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
# print(root.iter('year')) #全文搜索
# print(root.find('country')) #在root的子节点找,只找一个
# print(root.findall('country')) #在root的子节点找,找所有
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) #打印生成的格式
十二、configparser模块,加载某种特定格式的配置文件
配置文件如下:数据准备test.ini,类似于settings.py的样式
# 注释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',encoding='utf-8')
#删除整个标题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'))
基于上述方法添加一个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)
十三、hash介绍
# 1、什么叫hash:
hash是一种算法(3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法),
该算法接受传入的内容,经过运算得到一串hash值
# 2、hash值的特点是:
#2.1 只要传入的内容一样,得到的hash值必然一样=====>要用明文传输密码文件完整性校验
#2.2 不能由hash值返解成内容=======》把密码做成hash值,不应该在网络传输明文密码
#2.3 只要使用的hash算法不变,无论校验的内容有多大,得到的hash值长度是固定的
hash算法就像一座工厂,工厂接收你送来的原材料(可以用m.update()为工厂运送原材料),经过加工返回的产品就是hash值
import hashlib
m=hashlib.md5()# m=hashlib.sha256()
m.update('hello'.encode('utf8'))
print(m.hexdigest()) #5d41402abc4b2a76b9719d911017c592
m.update('alvin'.encode('utf8'))
print(m.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af
m2=hashlib.md5()
m2.update('helloalvin'.encode('utf8'))
print(m2.hexdigest()) #92a7e713c30abbb0319fa07da2a5c4af
'''
注意:把一段很长的数据update多次,与一次update这段长数据,得到的结果一样
但是update多次为校验大文件提供了可能。
'''
# 如何使用hash
import hashlib
m = hashlib.md5() # fc5e038d38a57032085441e7fe7010b0
# m = hashlib.sha256() # 936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af
m.update('hello'.encode('utf-8'))
m.update('world'.encode('utf-8'))
res = m.hexdigest() # hello world
print(res) # 只要传入的内容一样,得到的hash值必然一样:fc5e038d38a57032085441e7fe7010b0
m1 = hashlib.md5('he'.encode('utf-8'))
m1.update('llo'.encode('utf-8'))
m1.update('world'.encode('utf-8'))
res = m1.hexdigest()
print(res) # 只要传入的内容一样,得到的hash值必然一样:fc5e038d38a57032085441e7fe7010b0
以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。
import hashlib
# ######## 256 ########
hash = hashlib.sha256('898oaFs09f'.encode('utf8'))
hash.update('alvin'.encode('utf8'))
print (hash.hexdigest())#e79e68f070cdedcfe63eaf1a2e92c83b4cfb1b5c6bc452d214c1b7e77cdfd1c7
# 撞库的实例:
#模拟撞库:
# 截获的密文如下
cryptograph = 'aee949757a2e698417463d47acac93df'
# 猜测的密码如下
passwds=[
'alex3714',
'alex1313',
'alex94139413',
'alex123456',
'123456alex',
'a123lex',
]
# 做一个密码字典如下
import hashlib
dic = {}
for p in passwds:
res = hashlib.md5(p.encode('utf-8'))
dic[p] = res.hexdigest()
print(dic)
# 进行比对
for k,v in dic.items():
if v == cryptograph:
print('撞库成功,明文密码是:%s',k)
# 提示撞库的成本==>密码加盐,拆分密码前后加入混淆数据
import hashlib
m = hashlib.md5()
m.update('天王'.encode('utf-8'))
m.update('alex3714'.encode('utf-8'))
m.update('盖地虎'.encode('utf-8'))
print(m.hexdigest()) # ef606e983c958a13273eaad0b29a054c
"""
密码加盐:
'a'天l王e盖x地3虎714
"""
# 密码加盐的优点:
# 攻击思路:撞库密码
# md5可以反解
十四、suprocess模块
import subprocess
# 执行系统命令
obj = subprocess.Popen('ls /root',shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
print(obj) # <subprocess.Popen object at 0x000001FC3FCF6B80>
res = obj.stdout.read() # 正确的结果
print(res.decode('gbk'),type(res)) # b'' <class 'bytes'>
# err_res = obj.stderr.read() # 错误结果
# print(err_res.decode('gbk'),type(err_res)) # windows系统使用gbk
# print(err_res.decode('utf-8'),type(err_res)) # Linux系统使用utf-8
十五、logging模块
一 日志级别
CRITICAL = 50 #FATAL = CRITICAL
ERROR = 40
WARNING = 30 #WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0 #不设置
自下而上输出
import logging
# 一:日志配置
logging.basicConfig(
# 1、日志输出位置:1、终端 2、文件
# filename='access.log', # 不指定,默认打印到终端
# 2、日志格式
format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s',
# 3、时间格式
datefmt='%Y-%m-%d %H:%M:%S %p',
# 4、日志级别
# critical => 50
# error => 40
# warning => 30
# info => 20
# debug => 10
level=30,
)
# 二:输出日志
logging.debug('调试debug,工程师调试用想出现的信息')
logging.info('消息info,正常信息')
logging.warning('警告warn,可以这样但要注意了')
logging.error('错误error,程序出现了错误')
logging.critical('严重critical,可能导致程序崩溃')
''' 运行结果如下,默认级别为warning,默认打印到终端
# 注意下面的root是默认的日志名字
WARNING:root:警告warn,可以这样但要注意了
ERROR:root:错误error,程序出现了错误
CRITICAL:root:严重critical,可能导致程序崩溃
'''
2、日志配置字典
"""
logging配置
"""
import os
# 1、定义三种日志输出格式,日志中可能用到的格式化串如下
# %(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用户输出的消息
# 2、强调:其中的%(name)s为getlogger时指定的名字
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
test_format = '%(asctime)s] %(message)s'
# 3、日志配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
'test': {
'format': test_format
},
},
'filters': {},
'handlers': {
#打印到终端的日志
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'simple'
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,日志轮转
'formatter': 'standard',
# 可以定制日志文件路径
# BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
# LOG_PATH = os.path.join(BASE_DIR,'a1.log')
'filename': 'a1.log', # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
'other': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # 保存到文件
'formatter': 'test',
'filename': 'a2.log',
'encoding': 'utf-8',
},
},
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
},
'专门的采集': {
'handlers': ['other',],
'level': 'DEBUG',
'propagate': False,
},
},
}
# encoding=utf-8
# auther:lsj
"""
logging配置:日志配置字典LOGGING_DIC
"""
import os
# 1、定义三种日志输出格式,日志中可能用到的格式化串如下
# %(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用户输出的消息
# 2、强调:其中的%(name)s为getlogger时指定的名字,定义格式如下三种
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]'
simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
test_format = '%(asctime)s] %(message)s'
# 3、日志配置字典
LOGGING_DIC = {
'version': 1,# 版本
'disable_existing_loggers': False, # 关闭已存在的日志
'formatters': { # 记住 formatters名不可改
'standard': { # standard自定义个名字可以改,标准格式
'format': standard_format # format名字不可以改
},
'simple': { # simple 自定义名字可以修改,简单格式
'format': simple_format # format名字不可以改
},
'test': { # 自定义名字可以修改,最简单的格式
'format': test_format # format名字不可以改
},
},# 格式化
'filters': {},
# handlers是日志的接收者,不同的handler会将日志输出给不同的位置
'handlers': {
#打印到终端的日志
'console': { # console该key是可以修改的
'level': 'DEBUG', # 日志级别,level名字不能改
'class': 'logging.StreamHandler', # 打印到屏幕,key名字不能改
'formatter': 'simple' # key名字不能改
},
#打印到文件的日志,收集info及以上的日志
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,日志轮转
'formatter': 'standard',
# 可以定制日志文件路径
# BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # log文件的目录
# LOG_PATH = os.path.join(BASE_DIR,'a1.log')
'filename': 'a1.log', # 日志文件
'maxBytes': 1024*1024*5, # 日志大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
# 打印到其他位置
'other': {
'level': 'DEBUG',
'class': 'logging.FileHandler', # 保存到文件
'formatter': 'test',
'filename': 'a2.log', # 文件路径,在项目中这里路径os.path.json(os.path.dirname(os.path.dirname(__file__)),'log','a2.log')
'encoding': 'utf-8',
},
},# 记住
# loggers日志的产生者,产生的日志会传递给handler,然后控制输出
'loggers': {
#logging.getLogger(__name__)拿到的logger配置
'第一个日志的产生者:kkk': {
'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG', # loggers(第一层日志级别关限制)--->handlers(第二层日志级别关卡限制)
'propagate': False, # 默认为True,向上(更高level的logger)传递,通常设置为False即可,否则会一份日志向上层层传递
},
'第二个日志的产生者:bbb': {
'handlers': ['other',],
'level': 'DEBUG',
'propagate': False,
},
}, # 记住
}
# encoding=utf-8
# auther:lsj
# 接下来要做到的是:拿到日志的产生者
# 第一个日志的产生者:kkk
# 第二个日志的产生者:bbb
# 如何拿到kkk和bbb?
# (1)但是需要先导入日志配置字典LOGGING_DIC
import test025_05settings
import logging
# 以下的两种导入config意义相同
# import logging.config # 这种导入在使用的时候注意就得用logging.config
# from logging import config
from logging import config,getLogger
# 查看config在哪?
# import logging
# logging.config # AttributeError: module 'logging' has no attribute 'config'
# (2)加载配置
config.dictConfig(test025_05settings.LOGGING_DIC)
# 使用import logging.config导入方式使用如下
# logging.config.dictConfig(test025_05settings.LOGGING_DIC)
# (3)获取日志字典里的某一个方法
logger1 = getLogger('第一个日志的产生者:kkk')
# settings.LOGGING_DIC
# (4)产生日志,此时在终端和文件都产生了日志
logger1.info('这是一条info日志')
# 终端日志:[INFO][2020-04-17 15:12:49,191][test024_05src.py:32]这是一条info日志
# a1.log文件日志:[2020-04-17 15:12:49,191][MainThread:11492][task_id:第一个日志的产生者:kkk][test024_05src.py:32][INFO][这是一条info日志]
# 如果我只想往终端输出日志使用
logger2 = getLogger('第二个日志的产生者:bbb')
logger2.info('logger2只向终端输出一条info日志')
# 终端日志输出:[INFO][2020-04-17 15:19:11,047][test024_05src.py:32]这是一条info日志
# 日志名的命名
# 日志名是区分日志业务归属的一种非常重要的标识
# settings 里设置默认的日志名字
'': { # 如果用户找不大就默认使用它默认
'handlers': ['default', ],
'level': 'DEBUG',
'propagate': False,
},
# 在src文件中调用一个不存在的名字
logger4 = getLogger('fsa')
logger4.info("找不到名字就使用空的默认")
十六、re正则模块
1、什么是正则?
正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符,来描述字符或租字符串的方法,或者说正则就是用来描述一类事物的规则。(在python中,通过re模块实现)。正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行。
2、生活中处处都是正则
比武我们描述:4条腿。你可能 会想到四条腿的动物或者桌子,椅子等,继续描述:4条腿,活得,就只剩下四条腿的动物这一类了。
3、常用匹配模式(元字节)
# encoding=utf-8
# auther:lsj
# 正则re实例
import re
print(re.findall('\w','abc123*&^_= ')) # \w:字母数字下划线。运行结果:['a', 'b', 'c', '1', '2', '3', '_']
print(re.findall('\W','abc123*&^_= ')) # \W:非字母数字下划线。运行结果:['*', '&', '^', '=', ' ']
print(re.findall('\s','abc\t\n\r\f123*&^_= ')) # \s:匹配任意空白字符。运行结果:['\t', '\n', '\r', '\x0c', ' ']
print(re.findall('\S','abc\t\n\r\f123*&^_= ')) # \s:非空白字符。运行结果:['a', 'b', 'c', '1', '2', '3', '*', '&', '^', '_', '=']
print(re.findall('\d','abcASD123*&^_= ')) # \d:匹配任意数字。运行结果:['1', '2', '3']
print(re.findall('\D','abcASD123*&^_= ')) # \D:匹配任意非数字。运行结果:['a', 'b', 'c', 'A', 'S', 'D', '*', '&', '^', '_', '=', ' ']
print(re.findall('\Aabc','abcASD123abc*&^_= ')) # \A:匹配字符串开头。运行结果:['abc']
print(re.findall('\Aabc',' abcASD123abc*&^_= ')) # \A:匹配字符串开头。运行结果:[]
print(re.findall('abc',' abcASD123abcABC*&^_= ')) # 查找所有匹配的字符,运行结果:['abc', 'abc']
print(re.findall('a\Z',' abcASD123abcABC*&^_= ')) # 匹配字符串结束,运行结果:[]
print(re.findall('a\Z',' abcASD123abcABC*&^_= a')) # 匹配字符串结束,运行结果:['a']
print(re.findall('a\Z',"""
abc
ASD
123
abc
ABC*&^_=
a""")) # 匹配到换行前的字符串结束,运行结果:['a']
print(re.findall('^a',' abcASD123abcABC*&^_= a')) # 匹配字符串开头,运行结果:[]
print(re.findall('a$',' abcASD123abcABC*&^_= a')) # 匹配字符串结尾,运行结果:['a']
print(re.findall('^ab$',' abcASD123abcABC*&^_= a')) # 同时匹配字符串的开头和结尾,运行结果:[]
print(re.findall('^ab$','ab')) # 同时匹配字符串的开头和结尾,运行结果:['ab']
print(re.findall('^ab$','a b')) # 同时匹配字符串的开头和结尾,运行结果:[]
# 重复匹配:|.|*|?|.*|.*?|+|{n,m}|
print(re.findall('.','abc\n123*&^_= ')) # .:匹配除了\n之外任意一个字符,运行结果:['a', 'b', 'c', '1', '2', '3', '*', '&', '^', '_', '=', ' ']
print(re.findall('a.b','a1b a\nb a|b a\tb')) # .:匹配除了\n之外任意一个字符,运行结果:['a1b', 'a|b', 'a\tb']
print(re.findall('a.b','a1b a\nb a|b a\tb',re.DOTALL)) # .:匹配除了\n之外任意一个字符,使用:re.DOTALL之后才能匹配换行符,运行结果:['a1b', 'a\nb', 'a|b', 'a\tb']
print(re.findall('..','abc\n123*&^_= ')) # .:匹配除了\n之外任意两个字符,运行结果:['ab', '12', '3*', '&^', '_=']
# 2、* 左侧字符重复0此或无穷次,性格贪婪
# ab*:取得b可以是0到无穷,a是必须存在
print(re.findall('ab*','abc\n123*&^_= absd a abb abbbbbb abbhb bbbbb')) # 运行结果:['ab', 'ab', 'a',abb', 'abbbbbb', 'abb']
# 3、+:左侧字符重复1次或无穷次,性格贪婪
# a必须存在,b至少出现1此
print(re.findall('ab+','ab abb abbb abbbbb')) # 运行结果: ['ab', 'abb', 'abbb', 'abbbbb']
# 4、?:左侧字符重复0次或1次,性格贪婪
print(re.findall('ab?','a ab abb abbb abbbbb')) # 运行结果:['a', 'ab', 'ab', 'ab', 'ab']
# 5、{n,m},自定义左侧字符重复n次到m次,性格贪婪
# {0,} = * 0到无穷
# {1,} = + 1到无穷
# {0,1} = ? 0到1次
# {n} 单独一个n代表只出现n次,多一次不行少一次也不行。
# ab{2,5} = a必须存在,b至少存在2次到5次
print(re.findall('ab{2,5}','a ab abb abbb abbbbb')) # 运行结果:['abb', 'abbb', 'abbbbb']
# 练习:找出下列字符中所有的整数和小数
# .的体现\.
print(re.findall("\d+\.?\d*","ahfu9qwn22256.fjar7890t1.215gdgr54"))
# 6、[]匹配指定字符一个
# 如下字符串,找到ab之间存在1个数字的字符
print(re.findall('[a]\d{1}[b]','a|b a3b a26b a2548b aXb a b a\nb')) # ['a3b']
print(re.findall('[a]\d[b]','a|b a3b a26b a2548b aXb a b a\nb')) # ['a3b']
# 如下字符串,找到ab之间存在0-5个数字的字符
print(re.findall('[a]\d{0,5}[b]','a|b a3b a26b a2548b aXb a b a\nb')) # ['a3b', 'a26b', 'a2548b']
# 如下字符串,找到ab之间存在的是0-5的数字的字符
print(re.findall('a[501234][b]','a|b a3b a26b a2548b aXb a b a\nb')) # ['a3b']
print(re.findall('a[0-5][b]','a|b a3b a26b a2548b aXb a b a\nb')) # ['a3b']
# 如下字符串,找到ab之间存在的是0-9的数字或者a-z的字母的字符
print(re.findall('a[0-9a-zA-Z]b','a|b a3b a26b a2548b aXb a b a\nb')) # ['a3b', 'aXb']
# 如下字符串,找到ab之间存在的是0-9的数字或者a-z的字母的字符,
# []内有^代表取反
# ^代表以匹配的字符开头。
print(re.findall('a[^0-9a-zA-Z]b','a|b a3b a26b a2548b aXb a b a\nb',re.DOTALL)) # ['a|b', 'a b', 'a\nb']
# 如下字符串,找到a-b的字符,
print(re.findall('a-b',' a-b a|b a3b a26b a2548b aXb a b a\nb',re.DOTALL)) # ['a-b']
# 找出a-b 以及ab之间存在一个数字的字符
# -在[]内最左侧或者最右侧代表普通字符
print(re.findall('a[-0-9-\n]b',' a-b a|b a3b a26b a2548b aXb a b a\nb',re.DOTALL)) # ['a-b', 'a3b', 'a\nb']