Python 第四篇:生成器、迭代器、装饰器、递归函数与正则表达式
一:生成器:Generator,可以理解为是一种一个函数产生一个迭代器,而迭代器里面的数据是可以通过for循环获取的,那么这个函数就是一个生成器,即生成器是有函数生成的,创建生成器使用()表示,比如g = (x for x in range(10)),那么即创建一个名称为g的生成器,,如果外层是[]则表示是生成一个列表,生成器生成的序列可以使用next()方法获取其中的值:
1.__next__()方法获取序列中的值:
g1 = (x for x in range(3)) print(type(g1),"print(type(g1)的结果") print(g1,"print(g1)的结果") print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) 执行结果; <class 'generator'> print(type(g1)的结果 <generator object <genexpr> at 0x101b817d8> print(g1)的结果 0 1 2 如果是[]表示,则在使用__next__()取值的时候保存,会提示没有__next__()方法: g1 = [x for x in range(3)] print(g1.__next__()) 执行结果: Traceback (most recent call last): File "/Users/zhangshijie/PycharmProjects/S12-Python3/day4/11.py", line 5, in <module> print(g1.__next__()) AttributeError: 'list' object has no attribute '__next__'
2.__next__()获取文件中的行:
def func(): with open("test.txt") as f: try: while True: line = next(f) #每次读取一行,会自动换下一行 print(line,end='') except StopIteration: print("\n已经读取文件完成-->StopIteration执行") func() 执行结果: global log 127.0.0.1 local2 daemon maxconn 256 log 127.0.0.1 local2 info defaults log global mode http timeout connect 5000ms timeout client 50000ms timeout server 50000ms option dontlognull listen stats :8888 stats enable stats uri /admin stats auth admin:1234 已经读取文件完成-->StopIteration执行
3.如果生成器生成的序列中的值已经全部被__netx__()方法取出,但是后续还要获取数据的话,则会抛出StopIteration异常,如下:
g1 = (x for x in range(3)) print(type(g1),"print(type(g1)的结果") print(g1,"print(g1)的结果") print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) print(g1.__next__()) 执行结果: <class 'generator'> print(type(g1)的结果 <generator object <genexpr> at 0x101b817d8> print(g1)的结果 0 1 2 #在执行完成这一条数据的获取之后就报异常了 Traceback (most recent call last): File "/Users/zhangshijie/PycharmProjects/S12-Python3/day4/test.py", line 201, in <module> print(g1.__next__()) StopIteration
4.解决异常的办法:
g1 = (x for x in range(3)) for i in g1: try: print(type(g1),"print(type(g1)的结果") print(g1,"print(g1)的结果") print(g1.__next__(),"1") print(g1.__next__(),"2") print(g1.__next__(),"3") print(g1.__next__(),"4") print(g1.__next__(),"5") except StopIteration: #当捕获到指定的异常之后则执行以下的命令 print("数据已经或完成了") 执行结果: <class 'generator'> print(type(g1)的结果 <generator object <genexpr> at 0x1012817d8> print(g1)的结果 1 1 2 2 数据已经或完成了
二:迭代器,是一个可使用for循环遍历每一个元素的可迭代对象,之前学过的可迭代对象(Iterable)有以下几种,一是常见的数据类型,如列表list,元组tuple,字典dict,集合set,字串ztr等,二是生成器(generator)和yield的生成器方法(generator function),如果想知道一个对象是否是可迭代对象(Iterable),如:
1、使用isinstance()方法查看指定对象是否是可迭代对象,如:
from collections import Iterable print(isinstance((10),Iterable),"10是否可迭代对象") print(isinstance([],Iterable),"[]是否可迭代对象") print(isinstance((),Iterable),"()是否可迭代对象") print(isinstance({},Iterable),"{}是否可迭代对象") print(isinstance("abcd",Iterable),"字符串abcd是否可迭代对象") print(isinstance((x for x in range(10)),Iterable),"for生成的序列是否可迭代对象") 执行结果: False 10是否可迭代对象 True []是否可迭代对象 True ()是否可迭代对象 True {}是否可迭代对象 True 字符串abcd是否可迭代对象 True for生成的序列是否可迭代对象
2.使用isinstance()方法查看指定对象是否是迭代器,如:
from collections import Iterator print(isinstance((10),Iterator),"10是否迭代器") print(isinstance([],Iterator),"[]是否迭代器") print(isinstance((),Iterator),"()是否迭代器") print(isinstance({},Iterator),"{}是否迭代器") print(isinstance("abcd",Iterator),"字符串abcd是否迭代器") print(isinstance((x for x in range(10)),Iterator),"for生成的序列是否迭代器") g1 = (x * x for x in range(10)) print(isinstance(g1,Iterator),"g1生成的序列是否迭代器") 执行结果: False 10是否迭代器 False []是否迭代器 False ()是否迭代器 False {}是否迭代器 False 字符串abcd是否迭代器 True for生成的序列是否迭代器 True g1生成的序列是否迭代器
3.迭代器(Iterator)和迭代对象(Iterable)的区别:
1、可以从其中通过for循环获取数据的都是可迭代对象(Iterable)
2、可以通过next()方法获取数据的对象都是迭代器(Iterator)
3、迭代器只是在通过使用next()方法获取数据的时候才会计算,因此比可迭代对象效率更高并在内存使用率更低,因为可迭代对象是一次生成。
4、迭代器执行过程中无法后退到上一个或返回到指定的元素,其只能通过next()方法继续往前执行
5、for循环的readlines方法是一次读取所有行并将所有行放在一个列表当中,read()方法是读取所有内容到内存并保存为字符串格式
三:递归函数:
函数的内部可以调用其他的函数,但是如果这个函数的内部在执行的时候调用了函数自身,那么这个函数就是递归函数,因此递归函数一定是在函数里实现的,即先声明一个函数然后在函数内部中调用自己,递归函数需要有一个明确的退出接口,因为如果不设置退出接口则会进入无限循环调用的场景,最终可能会导致内存用尽等场景:
1.创建一个简单的递归函数:
def func(num): print(num) if num > 1: #先判断num的值大于1才继续执行 func(num -1) #在函数的原基础之上执行数值减1 func(10) #传递的值为10 执行结果: 10 9 8 7 6 5 4 3 2 1
2.使用递归函数计算斐波拉契(Fibonacci)算法
斐波拉契(Fibonacci)算法,即书第一个和第二个数值之外,这两个数值之后的任意一个数都是有前两位数值相加得出的,比如:0,1, 1, 2, 3, 5, 8, 13, 21, 34,55 ...这种结果如果用列表和for循环很不好计算,而且生成列表会生成很多用不到的数值,但是使用函数计算就比较简单了。
递归函数计算斐波那契算法的方法如下:
def func(num): x = 0 #定义三个变量 y = 0 z = 1 while x < num: #假如x的值小于函数调用的时候传递的num的数值 print(z) #打印z的结果 y,z = z,y + z #将y的等于z的值,z的值等于y+z的值,即完成了后面的一个数的值是有前两个数值相加的结果 x += 1 #然后将x的值自增1 print("每次相加分割线--------->") return z #将最终z的值返回给函数名,最后函数的值就等于z print(func(30)) 执行结果: 1 每次相加分割线---------> 1 每次相加分割线---------> 2 每次相加分割线---------> 3 每次相加分割线---------> 5 每次相加分割线---------> 8 每次相加分割线---------> 13 每次相加分割线---------> 21 每次相加分割线---------> 34 每次相加分割线---------> 55 每次相加分割线---------> 89 每次相加分割线---------> 144 每次相加分割线---------> 233 每次相加分割线---------> 377 每次相加分割线---------> 610 每次相加分割线---------> 987 每次相加分割线---------> 1597 每次相加分割线---------> 2584 每次相加分割线---------> 4181 每次相加分割线---------> 6765 每次相加分割线---------> 10946 每次相加分割线---------> 17711 每次相加分割线---------> 28657 每次相加分割线---------> 46368 每次相加分割线---------> 75025 每次相加分割线---------> 121393 每次相加分割线---------> 196418 每次相加分割线---------> 317811 每次相加分割线---------> 514229 每次相加分割线---------> 832040 每次相加分割线---------> 1346269
队规算法可用于解决以下问题:
- 数据的定义是按递归定义的,比如列表,Fibonacci函数)
- 问题解法可以安装按递归算法实现。(回溯)
- 数据的结构形式是按递归定义的。(比如树的遍历,图的搜索)
递归的缺点:递归算法解题的运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成内存栈溢出等。
函数返回值:
def func1(num): return 3,6,9,num #多个值返回给函数名 print(func1(10)) a = list(func1(10)) print(a) a,b,c,num = func1(11) #对函数返回值进行拆包,将返回值赋值给多个变量 print(a,"a从func1(11)执行结果获取到的值") print(b,"b从func1(11)执行结果获取到的值") print(c,"c从func1(11)执行结果获取到的值") print(num,"num从func1(11)执行结果获取到的值") 执行结果: (3, 6, 9, 10) #函数返回值默认是元组 [3, 6, 9, 10] #可以将元组转换为列表进行增删元素 3 a从func1(11)执行结果获取到的值 6 b从func1(11)执行结果获取到的值 9 c从func1(11)执行结果获取到的值 11 num从func1(11)执行结果获取到的值
四:装饰器:
装饰器本身是一个函数,是为了不改变其他函数的代码和用户调用方法的情况下其他函数添加之前没有的功能,如下是一个简单的装饰器:
1.装饰器的函数是传递固定参数:
def login(func): def inner(arg): print("通过验证") func(arg) #返回的是func形参的内存地址,这里func的实参是tc,因此返回了tc的内存地址 return inner @login def tv(name): print("欢迎%s登录tv界面" % name) tv = login(tv) #将tv作为函数login函数的执行参数传递过去 tv("jack")
2.装饰器的函数是传递动态参数:
def login(func): #此时的func等于tv的内存地址,而tv执行的时候需要两个参数*arg,**abc def inner(*arg,**abc): print("通过验证") return func(*arg,**abc) #返回的是func形参的内存地址,这里func的实参是tc,因此返回了tc的内存地址 return inner #返回的是 @login def tv(*arg,**abc): print("欢迎登录tv界面") tv = login(tv) #将tv作为函数login函数的执行参数传递给login函数 tv("jack","pass=123") 执行结果: 通过验证 通过验证 欢迎登录tv界面 执行流程: 1.进入到第一行代码login(func)放入内存,不执行函数内部 2.到装饰器@login 3.返回到login,即第一行,因为对函数进行了调用,将login重新定义为tv = login(tv),即把被装饰的函数做为参数传递给装饰器并再赋值给函数本身 4.进入login内部,执行第一层函数inner,放入内存 5.inner的执行结果是inner得到内存地址,得到此地址是为了不做验证,不然装饰器一被调用就通过了验证 6.执行tv = login(tv),将函数tv作为参数传递给函数login,而login是个装饰器 7.返回到第一行login(func) 8.执行内部函数inner 9.将innter函数放在内存 10.执行tv("jack","pass=123")login(func),将实参传递给inner 11.进入到inner。此时的innter的arg是arg:("jack","pass=123") abc={} 12.return func(*arg,**abc)的结果给inner 13.退回到inner函数 14..return func(*arg,**abc),此时的值已经给了inner 15.执行装饰器@login 16.打印欢迎界面
3.给装饰器传递参数:
def Before(request,kargs): #第一步将函数放进内存 #第六步,在装饰器之后执行函数 #第十步执行装饰器的内部函数的参数,这次是一个外部函数 print('before') #十一部,print结果 def After(request,kargs): #第二部放进第二个内存 #十二部执行另一个函数 print('after') def Filter(before_func,after_func): #第三部按照顺序放进第三个函数 #第五步执行装饰器,并将装饰器的参数执行,本次为两个函数 def outer(main_func): #第六到装饰器里面的函数 #第八步从return返回到这个函数 def wrapper(request,kargs): #第九步执行返回对应的函数,得到request,kargs的值 before_result = before_func(request,kargs) if(before_result != None): return before_result; main_result = main_func(request,kargs) if(main_result != None): return main_result; after_result = after_func(request,kargs) if(after_result != None): return after_result; return wrapper return outer #第七部,装饰器里面的第一个函数返回这个函数内部的函数名,这个函数名是个未执行的内存地址 #第十部 @Filter(Before, After) #第四部到装饰器界面 def Index(request,kargs): print('index') Index("aa","bb") 执行结果: before index after
五:python正则表达式基本方法:
正则表达式是处理字符串的一种工具,在很多语言里都支持,它是通过一定的规则语法,去指定的元素中匹配、查找和替换等操作字符串,在python里面是一个模块叫做re,通过import导入即可使用,re模块在python1.5版本添加,以下是是python 3.5.1当中的re模块常用方法:
1.compile(pattern, flags=0):将字符串转形式的正则表达式编译为Pattern对象,用于将常用的字符串转换成Pattern对象便于以后经常匹配这些字符串,然后返回RegexObject对象,最后可以通过RegexObject对象调用match()和search()方法,如:
import re reb = re.compile(r'abc') #将要匹配的字符串提前编译好,利于以后调用 rec = reb.match("abcd") #在abcd中匹配编译好的abc if rec: print(rec) print(rec.group(),"第一组匹配成功") else: print("第一组匹为匹配成功") rea = re.compile("abc") red = rea.match("ab") if red: print(red.group(),"第二组匹配成功") else: print("第二组未匹配成功") 执行结果: <_sre.SRE_Match object; span=(0, 3), match='abc'> #为使用group的结果,因此要查看匹配到的字符串要使用group查看 abc 第一组匹配成功 第二组未匹配成功
group():返回被正则表达式匹配成功的字符串
start():返回被正则表达式匹配成功字符串的开始下标位置
end():返回被正则表达式匹配成功的字符串结束的下标位置
snap():同时返回开始和结束位置
import re s1 = re.search("1","2nk11askd1") #search会从头到位匹配全部字符串当中是否有指定的字符串出现,在任意位置匹配成功都算成功 if s1: print(s1.group(),"第一组匹配成功") else: print("s1匹配失败") print() s2 = re.search("10","11034a10bc") #在中间和结果匹配成功都算是匹配成功的 if s2: print(s2.group(),"第一组匹配成功") print(s2.start(),"start返回开始的索引") print(s2.end(),"end返回结束的索引") print(s2.span(),"span返回开始和结束的索引") else: print("s2匹配失败")
2.escape(pattern):python3.4 新加,对字符串中的非字母数字进行转义,即对特殊字符和特殊符号做转义,在其前方加上反斜线\,如:
import re print(re.escape("www.test.com,#mail,xxx@xx.com")) 执行结果: www\.test\.com\,\#mail\,xxx\@xx\.com #在标点符号、特特殊字符前自动加上反斜线进行转义
3.findall(pattern, string, flags=0):以返回列表显示所有可以匹配到的结果,与search不同的是findall返回的是匹配到的所有结果,即返回的是不固定数量的结果,如:
import re s1 = re.findall("123","abc123abc123xy1234") #只匹配123 print(s1) 执行结果: ['123', '123', '123'] import re s1 = re.findall("12\d+","abc123abc123xy1234") #匹配12后面有一个或多个数值 print(s1) 执行结果: ['123', '123', '1234']
4.finditer(pattern, string, flags=0):匹配所有字符串,并返回一个迭代器,需要用for循环获取其中的值,如:
import re a1 = re.compile("bc") #编译好匹配对象,之匹配字符串bc s1 = a1.finditer("abc123abc123xy1234") for i in s1: print(i.group()) 执行结果: bc bc #第二个bc是在字符串的中间也可以匹配到
5.fullmatch(pattern, string, flags=0):判断匹配的字符串和目标字符串是否完全一致,是返回匹配group()成功的字串,否返回None,如:
import re s1 = re.fullmatch("password","password") if s1: print(s1.group(),"匹配成功") else: print("匹配失败") 执行结果: password 匹配成功 #字符串完整匹配成功
6.match(pattern, string, flags=0):match只从开头匹配第一个字符串,第一个未匹配就不再继续向后匹配,即使后面可以匹配到也不继续匹配,其只从第一位匹配指定的字符串,如果连续匹配上算成功,如果有一个匹配不上即返回匹配失败并返回None:
在python术语中,主要有两种方法完成模式匹配,搜索(searching)和匹配(matching),搜索是在字符串中的任意位置搜索匹配的模式,而匹配是指判断一个字符串是否从起始位置就全部匹配或部分匹配的某个模式,搜索是通过search()函数或方法实现,而匹配是调用match()函数或方法实现的:
import re s1 = re.match("1","12nkaskd1") #match只从开头匹配第一个字符串,第一个未匹配就不再继续向后匹配,即使后面可以匹配到也不继续匹配 if s1: print(s1.group(),"第一组匹配成功") else: print("s1匹配失败") s2 = re.match("10","12034abc") #即使后面有0也不匹配,其只从第一位匹配指定的字符串,如果连续匹配上算成功,如果有一个匹配不上即返回匹配失败 if s2: print(s2.group(),"第一组匹配成功") else: print("s2匹配失败") 执行结果;
7.purge():清空缓存中的正则表达式
import re s1 = re.compile("password+=") print(re.compile(s1)) print(re.purge()) 执行结果: re.compile('password+=') None
8.search(pattern, string, flags=0):search会从头到位匹配全部字符串当中是否有指定的字符串出现,在任意位置匹配成功都算成功,返回的是匹配成功的字符串:
search与findall的不同:
search是返回到的一个匹配到的结果,即使字符串当中有多个匹配结果也只返回一个,而且是返回的字符串格式
findall是返回的所有匹配结果,并将结果附加在一个列表当中
import re s1 = re.search("1","2nkaskd1") #search会从头到位匹配全部字符串当中是否有指定的字符串出现,在任意位置匹配成功都算成功 if s1: print(s1.group(),"第一组匹配成功") else: print("s1匹配失败") s2 = re.search("10","12bn1034abc") #在中间和结果匹配成功都算是匹配成功的 if s2: print(s2.group(),"第一组匹配成功") else: print("s2匹配失败")
执行结果: 1 第一组匹配成功 10 第一组匹配成功
#######search与findall的不同: import re s1 = re.findall("ab","2nabkabcddopaoab") #search会从头到位匹配全部字符串当中是否有指定的字符串出现,在任意位置匹配成功都算成功 if s1: print(s1,"第一组匹配成功") else: print("s1匹配失败") s2 = re.search("10","10102bn1034ab10c") #在中间和结果匹配成功都算是匹配成功的 if s2: print(s2.group(),"第一组匹配成功") else: print("s2匹配失败") 执行结果: ['ab', 'ab', 'ab'] 第一组匹配成功,findall是返回的所有匹配结果,并将结果附加在一个列表当中 10 第一组匹配成功,search是返回到的一个匹配打牌的结果,即使字符串当中有多个匹配结果也只返回一个,而且是返回的字符串格式
9.split(pattern, string, maxsplit=0, flags=0):split() 方法在用于在正则表达式中将字符串按照指定的分隔符进行切分,切分后将返回列表,方法同字符串的 split() 方法相似但提供更多的定界符,如:
import re a1 = re.compile(r"\(") s1 = a1.split("1+10-(10*3)") #search会从头到位匹配全部字符串当中是否有指定的字符串出现,在任意位置匹配成功都算成功 print(s1) 执行结果: ['1+10-', '10*3)'] #返回一个列表,以(对原字符串进行切分
10.sub(pattern, repl, string, count=0, flags=0):
import re a1 = re.compile(r"jack") #提前编译正常表达式,指定替换的字符串为jack s1 = a1.sub("tom","name is jack,name is jack,name is jack",count=2) #sub后面跟随将jack替换的目标字符串,和在哪里替换,可选参数count指定替换参数,默认为全部替换 s2 = a1.sub("tom","name is jack,name is jack,name is jack") #不指定替换次数 print(s1,"指定了2次替换") print(s2,"未指定替换次数") 执行结果: name is tom,name is tom,name is jack #指定了2次替换,超出次数一行将不再替换 name is tom,name is tom,name is tom 未指定替换次数
11.subn(pattern, repl, string, count=0, flags=0):subn() 方法类似于sub方法做字符串替换,但是返回的结果是替换后的一个元组,元组的前部分是替换后的结果,后部分是替换的次数,如:
import re a1 = re.compile(r"jack") #提前编译正常表达式,指定替换的字符串为jack s1 = a1.subn("tom","name is jack,name is jack,name is jack",count=2) #sub后面跟随将jack替换的目标字符串,和在哪里替换,可选参数count指定替换参数,默认为全部替换 s2 = a1.subn("tom","name is jack,name is jack,name is jack") #不指定替换次数 print(s1,"指定了2次替换") print(s2,"未指定替换次数") 执行结果: ('name is tom,name is tom,name is jack', 2) 指定了2次替换 ('name is tom,name is tom,name is tom', 3) 未指定替换次数
12.template(pattern, flags=0):和
import re s1 = re.template("21") #编译好要匹配的字串 print(s1.search("jack and tom is 21").group()) #对编译好的字串进行匹配 执行结果: 21 #只显示找到的字串
六:正则表达式匹配高级模式:
1.字符串匹配:
. :匹配任意单个字符,(换行符除外):
import re print(re.match("2.","1+2-3*2+(4-4+1/3)")) #matc从0个元素匹配,一次成功即成功,不成功则失败并返回None,matc返回的是直接匹配结果,不需要使用group() 查看匹配到的内容 执行结果: None ################# import re print(re.search("2.","1+2-3*2+(4-4+1/3)").group()) #search匹配成功后只显示一个匹配结果,而且需要使用group查看匹配到的结果 执行结果: 2- ################## import re print(re.findall("1.","1+2-3*2+(4-4+1/3)")) #findall 会在整个字符串匹配,并以列表结果显示匹配到的所有的结果
执行结果: ['1+', '1/']
^ :从字符串的头部开始匹配,可以匹配是否以指定字符开头:
import re print(re.findall("^1","1+2-3*2+(4-4+1/3)")) #search匹配成功后只显示一个匹配结果,而且需要使用group查看匹配到的结果
执行结果: ['1']
$ :匹配字符串的结束,可以匹配是否已指定字符串结束:
import re print(re.findall("1$","1+2-3*2+(4-4+1/3)-1")) print(re.findall("1$","1+2-3*2+(4-4+1/3)-2"))#匹配是否已指定字符串结尾,是返回匹配结果.否返回空列表 执行结果: ['1'] []
* :匹配前一个字符出现了0次或多次,即前面的字符可以没有匹配到也可以有无数个,这种操作符也称为Kleene(闭包操作):
import re print(re.findall("a*","1+a+b-2"))#匹配是否已指定字符串结尾,是返回匹配结果.否返回空列表
执行结果: ['', '', 'a', '', '', '', '', ''] #返回列表形式的结果,为匹配带的元素用空字串表示
+ :匹配前一个元素出现一次或多次,即至少要有一次,也可以有无数次,也被称为正闭包操作:
import re print(re.findall("a+","1+aaaba+b-2"))#匹配是否已指定字符串结尾,是返回匹配结果.否返回空列表 执行结果: ['aaa', 'a']
? :匹配前一个字符出现0次或1次,即可以1个也没有,也可以有1个,但是后面就不会匹配了,因为只会匹配一个没有有一个,不负责匹配两个:
import re print(re.findall("a?","1+aaaba+b-2"))#匹配是否已指定字符串结尾,是返回匹配结果.否返回空列表
执行结果: ['', '', 'a', 'a', 'a', '', 'a', '', '', '', '', ''] #单独匹配每个元素,未能匹配成功的元素以空字串表示
{N}:匹配前面的表达式指定次数,即强制匹配多少次:
import re print(re.findall("a{3}","1+aaaba+b-2"),"#指定前面的字符匹配多少次,匹配结果放在列表当中") print(re.findall("a{4}","1+aaaba+b-2"),"#指定前面的字符匹配多少次,超出范围后抛出空列表") 执行结果: ['aaa'] #指定前面的字符匹配多少次,匹配结果放在列表当中 [] #指定前面的字符匹配多少次,超出范围后抛出空列表
{M,N}: 匹配前面的字符出现M-N次
import re print(re.findall("a{1,3}","1+aaaba+b-2"),"#指定前面的字符匹配多少到多少次,匹配结果分开放在列表当中") print(re.findall("a{1,10}","1+aaaba+b-2"),"#指定前面的字符匹配多少到多少次,匹配结果分开放在列表当中,超出匹配范围不报错") 执行结果: ['aaa', 'a'] #指定前面的字符匹配多少次,匹配结果放在列表当中 ['aaa', 'a'] #指定前面的字符匹配多少次,超出范围后抛出空列表
[xxx] :匹配字符串当中的任意一个字符:
import re print(re.findall("a[abc]","1+aabbaba+b-2"),"匹配中括号当中的任意一个字符,即a后面可以是a可以是b也可以是c,中括号里的字符之间是或关系") print(re.findall("b[abc]","1+babababab"),"匹配中括号当中的任意一个字符,字符是或关系")
执行结果: ['aa', 'ab'] 匹配中括号当中的任意一个字符,字符是或关系 ['ba', 'ba', 'ba', 'ba'] 匹配中括号当中的任意一个字符,字符是或关系
[x-y]:匹配出现在中括号里的任意一个字符:
import re print(re.findall("a[a-z]","1+aabbaba+b-2"),"匹配a-z当中的任意一个字符") print(re.findall("b[b-d,f-z]","abacdbebz"),"匹配中括号当中b-d和f-z当中的字符,即把a和e不匹配,如此匹配则前方的ba和be都没有匹配到") 执行结果: ['aa', 'ab'] #匹配a-z当中的任意一个字符 ['bz'] #匹配中括号当中b-d和f-z当中的字符,即把a和e不匹配,如此匹配则前方的ba和be都没有匹配到
[^x-y]:取反,凡是出现在里面的字符都不进行匹配:
import re print(re.findall("a[^c-z]","1+aabbaba+b-2"),"匹配a-z当中的任意一个字符") print(re.findall("b[^b-d,f-z]","abacdbebz"),"与上次完全相反,这次出现在里面的不匹配,不出现的却被匹配,本次只匹配a和e")
执行结果: ['aa', 'ab', 'a+'] 匹配a-z当中的任意一个字符 ['ba', 'be'] 与上次完全相反,这次出现在里面的不匹配,不出现的却被匹配,本次只匹配a和e
[^xy]:不匹配出现在中括号里面的单个字符:
import re print(re.findall("a[^bcd]","1+aabbaba+b-2"),"a后面凡是出现b或c胡或d都不匹配") print(re.findall("b[^b,d-z]","abacdbcbebbz"),"b后面出现b,d-z的字符不匹配") 执行结果: ['aa', 'a+'] a后面凡是出现b或c胡或d都不匹配 ['ba', 'bc'] b后面出现b,d-z的字符不匹配
():对表达式进行分组:
import re s1 = re.match("(a(b)c)","abc") #对匹配进行分组, print(s1.group(1),"分组1") print(s1.group(2),"分组2") print(s1.groups(),"所有分组") 执行结果: abc 分组1 b 分组2 ('abc', 'b') 所有分组
2.特殊字符匹配:
\d:匹配任何数字,和0-9效果一致:
import re print(re.search("\da","abc1a").group()) #匹配任何一个数字后面跟了一个a 执行结果: 1a
\D:和\d完全相反,匹配任何非数字
import re print(re.search("\Da","abc1!@a").group()) #查找特殊字符后面带a的 执行结果: @a #匹配到的特殊字符
\w:匹配任何数字字母字符,同等于[0-9][a-z][A-Z]:
import re print(re.search("\wa","wabc1!@a").group()) #匹配任何一种字母数字和字符后后面带a的 执行结果: wa #匹配到的wa
\W:和\w完全相反,取[0-9][a-z][A-Z]以外的特殊字符:
import re print(re.findall("\Wa","wrabc1!1@a"),"\W只会匹配数字和大小写字母以外的特殊字符") print(re.findall("\Da","wrabc1!1@a"),"\D会匹配除数字以外的其他字符,包括字母和特殊字符") 执行结果: ['@a'] \W只会匹配数字和大小写字母以外的特殊字符 ['ra', '@a'] \D会匹配除数字以外的其他字符,包括字母和特殊字符
\s:匹配任何空白字符,等于匹配\n \t \r \v \f:
import re print(re.findall("\s","w rabc1!1@a"),"\s匹配空格") print(re.findall("\s","wrabc 1!1@\na"),"\s匹配空格和换行符\n") 执行结果: [' '] \s匹配到的空格元素 [' ', '\n'] \s匹配空格和换行符和空格
\S:与\s相反,匹配空白字符\n \t \r \v \f以外的字符
import re print(re.findall("\S","w rabc1!1@a"),"\S匹配空白符以外的字符,中间的空格给忽略了") print(re.findall("\S","wrabc 1!1@\na"),"\S匹配空白字符以外的字符,空格和\n给忽略了") 执行结果: ['w', 'r', 'a', 'b', 'c', '1', '!', '1', '@', 'a'] \S匹配空白符以外的字符,中间的空格给忽略了 ['w', 'r', 'a', 'b', 'c', '1', '!', '1', '@', 'a'] \S匹配空白字符以外的字符,空格和 给忽略了
\b:匹配单词边界:
import re print(re.findall(r"oy\b","tom jack is a boy!"),"匹配右侧是否单词边界,即有空格或其后面没有单词了,boy是最后一个单词") print(re.findall(r"om\b","tom jack is a boy!"),"匹配右侧是否单词边界,即有空格或其后面没有单词了,om是tom,右侧是空格") print(re.findall(r"\bom","tom jack is a boy!"),"om是tom,om的左侧还有一个t因此其左侧不是单词边界") 执行结果: ['oy'] 匹配右侧是否单词边界,即有空格或其后面没有单词了,boy是最后一个单词 ['om'] 匹配右侧是否单词边界,即有空格或其后面没有单词了,om是tom,右侧是空格 [] om是tom,om的左侧还有一个t因此其左侧不是单词边界
\B:与\b相反,匹配非单词边界,即这个单词不是以此开头的:
import re print(re.findall(r"oy\B","tom jack is a boy!"),"oy右侧是单词边界,因此不会匹配上") print(re.findall(r"\Bom","tom jack is a boy!"),"om左侧不是单词边界,因此可以匹配成功") print(re.findall(r"om\B","tom jack is a boy!"),"om右侧是边界,因此不会匹配成功") 执行结果: [] oy右侧是单词边界,因此不会匹配上 ['om'] om左侧不是单词边界,因此可以匹配成功 [] om右侧是边界,因此不会匹配成功
\nn:匹配已经保存的子组:
匹配手机号:
import re a = "mail:xxxx@aa.xxx.com,TEL:18600033300" b = "email:1669121886@qq.com" print(re.findall(r"\d[0-9]{10}",a)) print(re.findall(r"\d[0-9]{10}",b)) 执行结果: ['18600033300'] []
匹配邮箱地址:
import re a = "mail:xxxx@aa.xxx.com,TEL:18600033300" b = "email:1669121886@qq.com" print(re.findall("\w+@\w+\.*\w+\.com",a)) #\w+\.* 匹配*前面的字符0次或多次,本次匹配三级域名邮箱 print(re.findall("\w+@\w+\.*\w+\.com",b)) #本次匹配两级域名的邮箱 执行结果: ['xxxx@aa.xxx.com'] ['1669121886@qq.com']
match只从开头进行完整匹配,头部匹配成功才算成功,search相当于全行匹配,任意位置匹配成功都算成功,两种方法返回的是匹配到的对象,要使用group查看对象里面的结果
findall返回的是所有结果的一个列表,不需要使用group即可看到结果,也可以使用for循环遍历每一个元素