Python基础学习(19)re 模块Ⅱ、递归函数、带参数的装饰器
一、今日大纲
- re 模块
- 递归函数
- 带参数的装饰器
二、re 模块Ⅱ
-
split()
在 re 模块中,
split()
主要用于将一段字符串根据正则表达式特征匹配分离,也可以定义分组返回特定的匹配字符串。ret1 = re.split('d+', 'alex222wusir111taibai') ret2 = re.split('(d+)', 'alex222wusir111taibai') ret3 = re.split('d(d)d', 'alex222wusir111taibai') print(ret1) # ['alex', 'wusir', 'taibai'] print(ret2) # ['alex', '222', 'wusir', '111', 'taibai'] print(ret3) # ['alex', '2', 'wusir', '1', 'taibai']
-
sub()
和subn()
全称为 substitute,和字符串的 replace 操作类似,可以设定替换的次数;而
subn()
可以返回一个元组,元组内分别为替换后的匹配字段和替换次数。ret1 = re.sub('d+', 'H', 'alex123wusir456') ret2 = re.sub('d+', 'H', 'alex123wusir456', 1) ret3 = re.subn('d+', 'H', 'alex123wusir456') print(ret1) # alexHwusirH print(ret2) # alexHwusir456 print(ret3) # ('alexHwusirH', 2)
-
match()
在作用上等价于
search()
方法的正则表达式参数前人为地添加了一个^
;但是match()
一般用来表达规定字段,而search()
一般用来表达在字符串中寻找满足条件的子内容。ret1 = re.match('d+', 'eva123taibai456') # ret1 = re.search('^d+', 'eva123taibai456') ret2 = re.match('d+', '123eva456taibai') print(ret1) # None print(ret2) # <_sre.SRE_Match object; span=(0, 3), match='123'> print(ret2.group()) # 123
-
compile()
一个合格的项目,不仅仅需要满足功能需要,也需要足够的性能;时间复杂度和空间复杂度就是一个程序性能好坏的主要指标,比如因为效率过低,
list
在底层是不适合使用insert()
和pop(n)
操作的;回到 re 模块,加入一个正则表达式需要使用 多次,多次利用findall()
和research()
操作同一正则表达式,会影响效率,故我们使用compile()
操作,将读取正则表达式的操作集中在一起,可以提高程序的效率。ret = re.compile('d+') res1 = ret.search('alex37176') res2 = ret.findall('alex37176') print(res1) # <_sre.SRE_Match object; span=(4, 9), match='37176'> print(res2) # ['37176']
-
finditer()
与
complie()
操作降低时间复杂度类似,finditer()
会将寻找到的匹配字段用迭代器形式返回,可以极大的提升大文件匹配的空间性能,提升程序的效率。ret = re.finditer('d+', 'asdasdd312312') for i in ret: print(i.group()) # 312312
-
分组命名
有时由于正则表达式过长,且我们需要的分组过多,使用数字索引会比较困难且易读性差,这时我们可以将分组以
(?P<group_name>regrx)
的形式命名,然后通过.group('group_name')
的形式读取,增强程序的可读性,也便利了编程操作。ret = re.search('d(d)d(w+?)(d)(w)d(d)d(w+?)(d)(w)d(d)d(?P<name>w+?)(d)(w)', '123abc312321dasdsd1231233213123_213421sdddsa') print(ret.group(10)) # 一个一个数容易乱 print(ret.group('name')) # 一个一个数容易乱
-
分组命名的引用操作
# 分组命名的引用 # <tag_name>string</tag_name>写一个匹配标签的regex # 方法一,分组命名 exp = '<abc>dasdas&**Dh</abc>as&**Dh</abd>' ret = re.search('<(?P<tag>w+)>.*?</(?P=tag)>', exp) print(ret.group()) # <abc>dasdas&**Dh</abc> # 方法二,1引用,因为1在Python中有特殊意义,所以需要取消1的特殊意义 exp = '<abc>dasdas&**Dh</abc>as&**Dh</abd>' ret = re.search(r'<(w+)>.*?</1>', exp) # 将字符串加raw,取消1的特殊含义 print(ret.group()) # <abc>dasdas&**Dh</abc>
三、递归函数
递归函数就是在一个函数中调用函数本身。由于需要反复调用本身,就涉及到了递归深度的问题,在 Python 中,我们人为地规定了递归的深度为1000(也有写做998、997)。sys
模块中的sys.setrecursionlimit(n)
虽然可以修改递归的深度,但是由于递归需要反复调度,其本身的效率其实是较低的。但是递归传达了一种分治思想,可以把一个复杂的问题分解为一系列类似的简单问题解决。递归的逻辑具有两个重要概念:
①递归边界
②递归式(递归调用)
现在我们利用一个阶乘的例子来简单理解以下递归函数:
def factorial(i):
if i == 1: # 递归边界
return 1
return factorial(i-1) * i # 递归调用
中间传达的其实是下列思想:
factorial(n) = factorial(n-1) * n
factorial(n-1) = factorial(n-2) * (n-1)
factorial(n-2) = factorial(n-3) * (n-2)
....
factorial(2) = factorial(1) * 2
factorial(1) = 1
下面提供了一些题目,尝试用递归的思想解决。
# 2.os模块:查看一个文件夹下的所有文件,这个文件夹下面还有文件夹,不能用walk
import os
def read_dir(dirname):
if os.listdir(dirname) == []:
return
for i in os.listdir(dirname):
print(i)
if os.path.isdir(os.path.join(dirname, i)):
read_dir(os.path.join(dirname, i))
# read_dir('day20')
# 3.计算一个文件夹下所有文件的大小,这个文件夹下面还有文件夹,不能用walk
import os
size = 0
def read_dirsize(dirname):
if os.listdir(dirname) == []:
return
for i in os.listdir(dirname):
global size
size += os.path.getsize(os.path.join(dirname, i))
if os.path.isdir(os.path.join(dirname, i)):
read_dirsize(os.path.join(dirname, i))
# read_dirsize('day20')
# 4.计算斐波那契数列
def Fibonacci(n):
if n == 1 or n == 0:
return 1
return Fibonacci(n-1) + Fibonacci(n-2)
# print(Fibonacci(40))
# 5.三级菜单
menu = {
'北京': {
'海淀': {
'五道口': {
'soho': {},
'网易': {},
'google': {}
},
'中关村': {
'爱奇艺': {},
'汽车之家': {},
'youku': {},
},
'上地': {
'百度': {},
},
},
'昌平': {
'沙河': {
'老男孩': {},
'北航': {},
},
'天通苑': {},
'回龙观': {},
},
'朝阳': {},
'东城': {},
},
'上海': {
'闵行': {
"人民广场": {
'炸鸡店': {}
}
},
'闸北': {
'火车战': {
'携程': {}
}
},
'浦东': {},
},
'山东': {},
}
def read_menu(menu):
if menu == {}:
return
while True:
for i in menu:
print(i)
select = input('input>>')
if select.upper() == 'Q':
break
elif select in menu:
read_menu(menu[select])
else:
print('ILLEGAL INPUT')
read_menu(menu)
四、带参数的装饰器
装饰器的思想之前已经介绍过了,主要是满足开放封闭原则;我们提前写好一个功能,让别人需要的时候就能够直接使用完成相应的功能,不需要逐行修改代码。这里有一个装饰器非常典型的功能:写日志,即在项目的日志文件中,记录在什么时间,调用了什么函数,完成了什么操作等等;以购物车功能为例,我们每个操作都希望能够写下日志,但是不同类型的日志我们希望能够记录到不同的日志文件中,这时我们不必写两个装饰器,只需要将装饰器再包裹一层,用来传输装饰器的参数,达到在不同文件中写日志的目的。
# 登录和注册的信息 写道auth.log文件里
# 所有的购物信息,写道operate.log文件里
import time
def log(path):
def wrapper(func):
def inner(*args, **kwargs):
ret = func(*args, **kwargs)
with open(path, mode='a', encoding='utf-8') as f:
msg = '%s 执行了%s'%(time.strftime('%Y/%m/%d %H:%M:%S'), func)
f.write('
' + msg)
return ret
return inner
return wrapper
# 装饰器不带参数相当于
# login = log(login)
# 装饰器带参数相当于
# ret = log('auth.log')
# login = ret(login)
@log('auth.log')
def login():
print('login')
@log('auth.log')
def register():
print('register')
@log('operate.log')
def show_goods():
print('show goods')
@log('operate.log')
def add_goods():
print('add goods')
根据上面的例子,我们可知带参数的装饰器基本形式为:
def decorator(args):
def wrapper(func):
def inner(*args, **kwargs):
"""
在函数运行之前添加的额外功能
"""
ret = func(*args, **kwargs)
"""
在函数运行之后添加的额外功能
"""
return ret
return inner
return wrapper