叠加多个装饰器分析 yield表达式形式 生成式 递归函数
1 多个装饰器的执行顺序
函数外包裹函数的目的是为了向内部函数wrapper传参。
最终都是将原函数func_test换成wrapper函数。
装饰器的加载顺序从下到上。
装饰器的执行顺序从上到下。
# 3. func1指向的是原函数wrapper2的内存地址
def deco1(func1):
print('deco1 start')
def wrapper1(*args, **kwargs):
print('wrapper1 start')
res = func1() # 4 func1是wrapper2
print('wrapper1 stop')
return res
print('deco1 stop')
return wrapper1
# 2. func2指向的是原函数wrapper3的内存地址
def deco2(func2):
print('deco2 start')
def wrapper2(*args, **kwargs):
print('wrapper2 start')
res = func2() # 5 func2是wrapper3
print('wrapper2 stop')
return res
print('deco2 stop')
return wrapper2
# 1. func3指向的是原函数func_test的内存地址
def deco3(func3):
print('deco3 start')
def wrapper3(*args, **kwargs):
print('wrapper3 start')
res = func3() # 6 func3是原函数func_test
print('wrapper3 stop')
return res
print('deco3 stop')
return wrapper3
# 装饰器的加载顺序从下到上。
@deco1
# func_test = deco1(func_test)
# => func_test = deco1(wrapper2)
# => func_test = wrapper1
@deco2
# func_test = deco2(func_test)
# => func_test = deco2(wrapper3)
# => func_test = wrapper2
@deco3
# func_test = deco3(func_test)
# => func_test = wrapper3
def func_test():
print('func_test')
# 装饰器的执行顺序从上到下。
func_test()
'''
deco3 start
deco3 stop
deco2 start
deco2 stop
deco1 start
deco1 stop
wrapper1 start
wrapper2 start
wrapper3 start
func_test
wrapper3 stop
wrapper2 stop
wrapper1 stop
'''
2 yield表达式形式
变量 = yield 返回值
函数内存在yield语句,调用这个函数不会执行代码,返回一个生成器。
通过生成器的next()方法触发函数体代码的运行,直到yield语句,返回yield后面的表达式。
再通过next或send方法触发函数体代码的运行,直到下一个yield语句,依此类推。
如果代码后面无yield语句,则抛出StopIteration异常。
def func(p1):
print('开始执行。')
while 1:
p2 = yield '迭代结果'
print(f'{p1} - {p2}')
gen = func(1)
# gen.send('测试') 报错,不能为一个新生成器传值
res1 = next(gen) # 开始执行。
print(res1) # 迭代结果
res2 = next(gen) # 1 - None
print(res2) # 迭代结果
res3 = gen.send(None) # 1 - None
print(res3) # 迭代结果
res4 = gen.send('2') # 1 - 2
print(res4) # 迭代结果
gen.close()
next(gen) # 抛出异常 StopIteration
- 生成器的方法send主要用于向生成器对象传值。
send()的参数会成为yield返回的值,可以将其赋值给yield等号左边的变量。
yield后面的表达式会作为迭代器的本次迭代结果返回。 - 函数send只接受一个参数。
- gen.send(None) 等同于 next(gen)。
- gen.close()用于关闭一个迭代器,之后如果再调用next或send方法则会抛出StopIteration异常。
3 三元表达式 生成式
3.1 三元表达式
def func(x, y):
if x > y:
return x
else:
return y
三元代表三个要素:
判断条件,条件成立时的结果,条件不成立时的结果
条件成立时的结果 if 条件 else 条件不成立时的结果
def func(x, y):
res = x if x > y else y
return res
3.2 生成式
3.2.1 列表生成式
[表达式 for 元素 in 可迭代对象 if 条件]
lst = ['a', '123', 'cc22c']
new_lst = []
for each in lst:
if each.isdigit():
new_lst.append(int(each))
print(new_lst) # [123]
lst = ['a', 'bb', 'ccc']
new_lst = [int(each) for each in lst if each.isdigit()]
print(new_lst) # [123]
3.2.2 字典生成式
{键 : 值 for 元素 in 可迭代对象 if 条件}
old_dict = dict(a=1, A=2, b=3, c=4, C=5)
print(old_dict) # {'a': 1, 'A': 2, 'b': 3, 'c': 4, 'C': 5}
# 大小写的key值合并,统一以小写输出
new_dict = {each_key.lower() : old_dict.get(each_key.lower(), 0) + old_dict.get(each_key.upper(), 0) for each_key in old_dict}
print(new_dict) # {'a': 3, 'b': 3, 'c': 9}
3.2.3 集合生成式
{表达式 for 元素 in 可迭代对象 if 条件}
3.2.4 生成器表达式
没有元组生成式
(表达式 for 元素 in 可迭代对象 if 条件)
得到生成器
gen = (i for i in range(10) if i < 2)
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 抛出异常StopIteration
应用 统计文件字符数
with open(r'./db.txt', mode='rt', encoding='utf-8') as f:
total_num = sum(len(each_line) for each_line in f)
print(total_num)
生成器表达式得到的是生成器,属于可迭代对象。
sum(表达式 for 元素 in 可迭代对象 if 条件)
list(表达式 for 元素 in 可迭代对象 if 条件)
4 递归函数
4.1 什么是递归函数
也可以称为函数的递归调用,是函数嵌套调用的一种特殊形式。
指的是在在调用一个函数的过程中直接或间接地调用这个函数本身。
直接调用自己
def func():
func()
func()
间接调用自己
def func1():
func2()
def func2():
func1()
func1()
函数每调用一次都会产生自己的局部空间。
如果允许无限地递归调用,则会无限地申请内存空间,这样会耗尽可用内存空间。python解释器对递归调用深度进行了限制,默认是1000层。
sys.getrecursionlimit() # 1000
sys.setrecursionlimit(2000) # 设置为2000层
4.2 为什么使用递归函数
递归的本质是循环,使用目的是重复执行一段代码。
将一段代码循环运行的方案
while 1:
代码
def func():
代码
func()
func()
递归调用不应该无限地进行下去,需要在满足一定条件后结束调用。
使用return语句来结束调用。
4.3 递归调用的两个阶段
回溯阶段 和 递推阶段
递归调用中,逐层深入调用自己的阶段称为回溯阶段。
在满足一定条件后函数在某一层调用过程中会结束递归调用,从那一层开始逐层向外返回的阶段称为递推阶段。
有关列表的应用
lst = [1, 2, [3, 4, [5, [6], 7]], 8, 9]
def print_elem(lst):
for elem in lst:
if type(elem) is list:
print_elem(elem)
else:
print(elem)
print_elem(lst)
5 练习
5.1
文件内容如下
egon male 18 3000
alex male 38 30000
wupeiqi female 28 20000
yuanhao female 28 10000
要求:
- 从文件中取出每一条记录放入列表中,
列表的每个元素都是{‘name’:‘egon’,‘sex’:‘male’,‘age’:18,‘salary’:3000}的形式; - 计算所有人的薪资之和;
- 取出所有的男人的名字;
- 将每个人的信息中的名字映射成首字母大写的形式;
- 过滤掉名字以a开头的人的信息。
with open(r'./test.txt', mode='rt', encoding='utf-8') as f:
# value_lst = [each_line.strip().split(' ') for each_line in f]
key_lst = ['name', 'sex', 'age', 'salary']
user_lst = [{
key_lst[0]: each_list[0],
key_lst[1]: each_list[1],
key_lst[2]: each_list[2],
key_lst[3]: each_list[3]
} for each_list in
[each_line.strip().split(' ') for each_line in f]
]
print(user_lst)
# 计算所有人的薪资之和
salary_sum = sum(int(each_dict['salary']) for each_dict in user_lst)
print(salary_sum)
# 取出所有的男人的名字
male_name_lst = [each_dict['name'] for each_dict in user_lst if each_dict['sex'] == 'male']
print(male_name_lst)
# 将每个人的信息中的名字映射成首字母大写的形式 captalize
capitalize_name_lst = [each_dict['name'].capitalize() for each_dict in user_lst]
print(capitalize_name_lst)
# 过滤掉名字以a开头的人的信息 startswith
target_name_lst = [each_dict['name'] for each_dict in user_lst if not each_dict['name'].startswith('a')]
print(target_name_lst)
5.2 斐波那契数列
使用递归打印斐波那契数列,即数列中一个数是前两个数的和。
def get_fib_num(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return get_fib_num(n - 1) + get_fib_num(n - 2)
for i in range(10):
print(get_fib_num(i), end=' ')
5.3
一个嵌套很多层的列表,如 l=1,2,[3,[4,5,6,[7,8,[9,10,[11,12,13,[14,15]]]]]]],用递归取出所有的值。
l = [1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15]]]]]]]
def print_elem(l):
for each_elem in l:
if type(each_elem) is list:
print_elem(each_elem)
else:
print(each_elem, end=' ')
print_elem(l)