day18:正则表达式和re模块
1,复习递归:返回值,不要只看到return就认为已经返回了,要看返回操作是在递归的第几层发生的,然后返回给了谁,如果不是返回给最外层函数,调用者就接收不到,需要再分析,看如何把结果返回回来,超过最大递归限制的报错,只要写递归,必须要有结束条件
2,几乎所有的递归问题都可以用循环来解决问题,只不过有时候,循环考虑的事情更多更复杂,递归会相对少一些,循环写好了,效率有时候会比递归好一点,有时候需要想太多问题,所以就用递归,比如快排,递归和算法联系很紧密,会把问题变简单,效率高。平时递归用的不多,除非,算法分析什么的,如果未来不做这些,工作的很长一段时间都是用不到的。面试有时候会考,递归是非常经典的计算机算法和思想,能看懂,能掌握几个经典的数学算法和概念就可以了
3,作业题一,斐波那契数列递归实现,一定要注意结束条件和传递,递归一般都是结果往前推的
# 1,1,2,3,5,8,13,21,34,55 # 第一种,树性结构 def fib(n): if n == 1 or n ==2: return 1 # 结束条件 else: return fib(n-1) +fib(n-2) print(fib(10)) # 循环实现 def fib(n): num1, num2 = 1,1 num = 0 if n==1 or n==2: return 1 else: for i in range(n-2): num = num1 + num2 num1 = num2 num2 = num return num print(fib(8)) # 第三种 # 一次调用,那就返回两个值 def fib(n,l=[0]): # 默认参数的陷阱问题,无论发生多少次调用,这个地方都只是用这一个值,利用了这个陷阱问题,在不同的函数调用之间传递值 l[0] +=1 print(l[0]) if n == 1: return 1 if n ==2 : l[0]-=1 return 1,1 else: a,b = fib(n-1) l[0]-=1 print(l[0]) # 这只是个计数器,计算调用了多少次,最后减完了就可以返回结果了 # a,b = b,a+b if l[0] == 0: return a+b return b,a+b print(fib(11))
4,发生一次调用和两次调用的会非常慢,因为是树形调用,2的n次方和n次的区别
5,作业题二,阶乘,注意fac1001会超过最大递归限制
# 作业题二:阶乘factorial 1 1*2 # 循环实现 def fac(n): if n == 1: return 1 else: b = 1 for i in range(1,n+1): b = i * b return b print(fac(4)) # 递归实现 def fac(n): if n == 1: return 1 else: return n*fac(n-1) print(fac(3))
6,re模块和正则表达式,对比time模块和时间,时间是现实存在的,re模块背后已经存在的就是正则表达式,首先学习正则表达式,然后学习正则表达式操作正则,正则表达式是用来做字符串匹配的,从一大段文章中匹配首先要学习规则,想要Python来做这件事,就要用到re模块。
老师的博客地址:https://www.cnblogs.com/Eva-J/articles/7228075.html#_label10
7,比方说你在注册一个网站,他会检测你输入的格式是不是有问题,这些都是正则来实现的
8,正则规则是客观存在的,世界上任何一种语言都可以有正则的,正则规则在处理字符串上有非常大的优势,正则里面所有的东西,Python里面翻译过来都是字符串,看一个简单的例子
# 判断手机号码是否合法 # 如果是从整个文件里面找出所有符合条件的手机号码,显然这样处理会很麻烦,而正则表达式却可以轻松的处理掉,他有先天的优势 while True: phone = input("please input you phone number:") if len(phone) == 11 \ and phone.isdigit() \ and (phone.startswith('13')\ or phone.startswith('14') \ or phone.startswith('15') \ or phone.startswith('18')): print("合法的手机号") else: print('不合法') # 正则表达式实现 import re phone = input("please input you phone number:") if re.match('^(13|14|15|18)[0-9]{9}$',phone): # 正则规则 print("合法的手机号") else: print('不合法')
9,在同一个位置可能出现的字符范围的时候,正则表达式的用武之地就来了
10,用Python来处理正则表达式用到了re模块,三个函数用的最多查找,findall,search,match,
import re s = 'jasdhfieafy73o84&……%o734yfuye*bfjb98u833242jjxb%' ret = re.findall('a', s) # ['a', 'a'],返回所有匹配到的结果放在列表中。 print(ret) ret = re.findall('[a-z]+',s) print(ret) # ['jasdhfieafy', 'o', 'o', 'yfuye', 'bfjb', 'u', 'jjxb']
11,search函数返回第一个找到的匹配值
s = 'jasdhfieafy73o84&……%o734yfuye*bfjb98u833242jjxb%' ret = re.search('a',s) # <re.Match object; span=(1, 2), match='a'> print(ret) # 返回一个结果的对象 # 从前往后找到一个就返回需要调用group来拿结果,找不到会返回None,不可以用group来取值,会报错。 print(ret.group()) # a # 一般情况下我们会,找到值再调用group函数 if ret: print(ret.group())
12,match函数,必须从头开始匹配
s = 'jasdhfieafy73o84&……%o734yfuye*bfjb98u833242jjxb%' ret = re.match('a',s) #从头开始匹配,如果正则规则从头开始可以匹配上,就返回一个变量,匹配的内容需要调用group才能显示, # 如果没有匹配上,就返回None,调用group会报错 print(ret) # None ret = re.match('j',s).group() print(ret) # j ret = re.match('ja',s).group() print(ret) # ja
if ret: print(ret.group())
13,split函数,除了查找之外其他的函数
ret = re.split('[ab]','abcd') print(ret) # ['', '', 'cd'] # 先按照a分割得到''和'bcd' 再按照b在split ret = re.split('ab','abcd') print(ret) # ['', 'cd'] # 按照ab来整体分割
14,sub函数
s = 'fkdjh9845tbfdog7720834y83hdiub' ret = re.sub('\d','$',s) # 和replace很像,但是replace不能运用正则 print(ret) print(s) ret = re.sub('\d','$',s,3) # 默认的是全部替换,如果我只想替换指定次数 print(ret) print(s)
15,subn函数
s = 'fkdjh9845tbfdog7720834y83hdiub' ret = re.subn('\d','$',s) print(ret) # 返回元祖,替换的结果,替换了多少次。
16,compile函数,一次编译,多次运用。将正则表达式编译成为一个正则表达式对象,内置函数也有compile,对一个字符串类型的代码提前进行编译,就可以用eval或者exec来运行了。
正则也是字符串,所以这里的compile和字符串是很像的。
s1 = 'fkdjh9845tbfdog7720834y83hdiub' s2 = '7720834y83hdiub' obj = re.compile('\d{3}') #匹配三个数字 ret = obj.search(s1) if ret: print(ret.group()) ret = obj.search(s2) if ret: print(ret.group()) # 运行结果: 984 772
17,finditer方法,迭代器里面的每一项都需要用i.group来查结果
s = 'fkdjh9845tbfdog7720834y83hdiub' ret = re.finditer('\d',s) # 返回一个存放匹配结果的迭代器 print(ret) #<callable_iterator object at 0x101eb2278> print(next(ret).group()) #查看第一个结果 print(next(ret).group()) # 查看第二个结果 print([i.group() for i in ret]) # 查看剩余的所有结果 print('----------------') for i in ret: print(i.group()) # 迭代器没有强转的过程,上面取过一次了,这儿不再取了
18,group取分组内的内容,用序号来取,这个取分组的机制是由Python决定的,和正则表达式没有关系的,这样的话就感觉比findall好了
import re ret = re.search('^[1-9]\d{14}(\d{2}[0-9x])?$','331182199400400500') print(ret.group()) print(ret.group(1)) ret = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','331182199400400500') print(ret.group()) print(ret.group(1)) print(ret.group(2)) # 取分组里面的内容 运行结果: 331182199400400500 500 331182199400400500 31182199400400 500
19,findall分组优先和取消分组优先
ret = re.findall('www.(baidu|lisa).com','www.lisa.com') print(ret) #['lisa'] # 这是因为findall会优先把匹配结果组里的内容返回,如想要要取消分组优先看下面 # 这个规则专门为取消分组优先做的 ret = re.findall('www.(?:baidu|lisa).com','www.lisa.com') # ?: print(ret) #['www.lisa.com']
20,split函数的分组优先
# 只要有分组,就会特殊对待分组里面的内容 s = 'sdjhfsdig123dfbirisb5df,jbfhj888dnf' ret = re.split('\d+',s) print(ret) # ['sdjhfsdig', 'dfbirisb', 'df,jbfhj', 'dnf'] ret = re.split('(\d+)',s) # 加到一个分组里面,会保留切掉的内容 print(ret) # ['sdjhfsdig', '123', 'dfbirisb', '5', 'df,jbfhj', '888', 'dnf']
21,爬虫小练习
# 简单版本 import re from urllib.request import urlopen def get_page(url): response = urlopen(url) return response.read().decode('utf-8') def parse_page(s): # 这个正则可以对照着html源码来看,re.S匹配空格 ret = re.findall( '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',s,re.S) return ret def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num # 这儿是由网址决定的 response_html = get_page(url) ret = parse_page(response_html) print(ret) count = 0 for i in range(10): # 10页 main(count) count += 25
22,爬虫复杂版本,链接,分析图片,登录,这些都是后话,以后再说,宗旨就是爬取下来,分析
# 复杂版本 import re from urllib.request import urlopen import json def get_page(url): response = urlopen(url) return response.read().decode('utf-8') # 拿到字节码,编码 def parse_page(s): # 这个正则可以对照着html源码来看,re.S匹配空格,联系上下文的匹配,只要分组里面的内容,利用分组优先,只提取出来,非贪婪匹配, # 匹配到什么时候为止,分组名指代分组内匹配到的内容,?P就代表给分组取名字 com = re.compile( '<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>' '.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>') ret = com.finditer(s,re.S) # 返回迭代器,循环迭代器 for i in ret: yield { "id":i.group("id"), "title":i.group("title"), "rating_num":i.group("rating_num"), "comment_num":i.group("comment_num") } def main(num): url = 'https://movie.douban.com/top250?start=%s&filter=' % num # 这儿是由网址决定的 response_html = get_page(url) ret = parse_page(response_html) # 返回的是一个生成器 print(ret) f = open('move_info7','a',encoding='utf8') for obj in ret: print(obj) data = json.dumps(obj,ensure_ascii=False) f.write(data+'\n') # json序列化是day20的内容,到时候会有详细的讲解 if __name__ == '__main__': count = 0 for i in range(10): # 10页 main(count) count += 25 # url 从网页上把代码爬取下来 # bytes -utf8,网页内容就是待匹配的内容 # ret = re.findall(正则,待匹配的字符串),ret 所有匹配到内容的列表 # 持续爬,过一段时间爬取一次
23,计算器大作业,老师讲解后在更新
# 基本思路: # 首先得到一个字符串 # 去空格 # 没有空格的字符串 # 先算最里层括号里面的:找括号,且括号里没有括号 # 得到了一个没有括号的表达式:只有加减乘除,从左到右先找到一个乘除法 ---重复 # 所有的乘除法都做完了 # 计算加减 --加减法 # 只有一个数了,就可以结束了
24,计算机大作业,跟着老师讲解自己写的代码
import re # 计算乘除 def cal_ret_son_1(ret): if '/' in ret: new_ret = ret.split('/') s = str(float(new_ret[0]) / float(new_ret[1])) return s elif '*' in ret: new_ret = ret.split('*') s = str(float(new_ret[0]) * float(new_ret[1])) return s # 计算加减 # 这个函数可以简化的其实 def cal_ret_son_2(ret): if '+' in ret: new_ret = ret.split('+') s = str(float(new_ret[0]) + float(new_ret[1])) return s elif '-' in ret: new_ret = ret.split('-') s = str(float(new_ret[0]) - float(new_ret[1])) return s # 第三步,计算没有括号的表达式 def cal_express_no_bracket(exp): exp = exp.strip('()') # 去掉两边的括号 # 先乘除后加减 while True: ret = re.search('\d+\.?\d*[*/]-?\d+\.?\d*', exp) if ret: ret_son = ret.group() s = cal_ret_son_1(ret_son) exp = exp.replace(ret_son,s) dealwith_minus(exp) else:break while True: exp = dealwith_minus(exp) ret = re.search('\d+\.?\d*[+\-]-?\d+\.?\d*', exp) if ret: ret_son = ret.group() s = cal_ret_son_2(ret_son) exp = exp.replace(ret_son, s) dealwith_minus(exp) else:break # ret = re.findall('-?\d+\.\d*',exp) # sum = 0 # for i in ret: # sum += float(i) return exp # 第三步骤,处理+-连着的这种情况 def dealwith_minus(exp): if "+-" in exp: exp = exp.replace('+-','-') elif "--" in exp: exp = exp.replace('--','+') return exp # 第二步 提取括号里面没有其他括号的表达式 # findall search match 三种方法供选择 # findall没有search好,为何呢?比如说3*5*6+2*3 他只能匹配到3*5和2*3,match只能匹配从头开始的 def remove_bracket(new_express): while True: express_no_brackets = re.search('\([^()]+\)',new_express) # 中间可以匹配任意多个不是括号的 if express_no_brackets: express_no_brackets = express_no_brackets.group() exp = cal_express_no_bracket(express_no_brackets) new_express = new_express.replace(express_no_brackets,exp) new_express = dealwith_minus(new_express) else: break ret = cal_express_no_bracket(new_express) return ret express = '1 - 2 * ( (60-30 +(-40/5*8) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' print(eval(express)) # 用来验证结果是否正确,会有一点点的误差 # 第一步 去空格 new_express = express.replace(' ','') print(remove_bracket(new_express)) # 用函数封装以后,代码明显清晰了很多
import re express = '1 - 2 * ( (60-30 +(-40/5*8) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' print(eval(express)) # 用来验证结果是否正确,会有一点点的误差 # 第一步 去空格 new_express = express.replace(' ','') # 计算乘除 def cal_ret_son_1(ret): if '/' in ret: new_ret = ret.split('/') s = str(float(new_ret[0]) / float(new_ret[1])) return s elif '*' in ret: new_ret = ret.split('*') s = str(float(new_ret[0]) * float(new_ret[1])) return s # 计算加减 def cal_ret_son_2(ret): if '+' in ret: new_ret = ret.split('+') s = str(float(new_ret[0]) + float(new_ret[1])) return s elif '-' in ret: new_ret = ret.split('-') s = str(float(new_ret[0]) - float(new_ret[1])) return s # 第三步,计算没有括号的表达式 def cal_express_no_bracket(exp): exp = exp.strip('()') # 去掉两边的括号 # 先乘除后加减 while True: dealwith_minus(exp) ret = re.search('\d+\.?\d*[*/]-?\d+\.?\d*', exp) if ret: ret_son = ret.group() s = cal_ret_son_1(ret_son) exp = exp.replace(ret_son,s) else:break while True: exp = dealwith_minus(exp) ret = re.search('\d+\.?\d*[+\-]-?\d+\.?\d*', exp) if ret: ret_son = ret.group() s = cal_ret_son_2(ret_son) exp = exp.replace(ret_son, s) else:break return exp # 第三步骤,处理+-连着的这种情况 def dealwith_minus(exp): if "+-" in exp: exp = exp.replace('+-','-') elif "--" in exp: exp = exp.replace('--','+') return exp # 第二步 提取括号里面没有其他括号的表达式 # findall search match 三种方法供选择 # findall没有search好,为何呢?比如说3*5*6+2*3 他只能匹配到3*5和2*3,match只能匹配从头开始的 while True: express_no_brackets = re.search('\([^()]+\)',new_express) # 中间可以匹配任意多个不是括号的 if express_no_brackets: express_no_brackets = express_no_brackets.group() exp = cal_express_no_bracket(express_no_brackets) new_express = new_express.replace(express_no_brackets,exp) new_express = dealwith_minus(new_express) else: break ret = cal_express_no_bracket(new_express) print(ret)