day13-三元表达式,生成式,递归函数,匿名函数
其实学到这,函数部分的精髓你都已经学的差不多了,那今天的内容就再讲函数的其他方法使用介绍
今天讲的内容有,三元表达式,列表生成式,字典生成式集合生成式以及生成器表达式(生成器生成式),还有递归函数和匿名函数.
别看介绍这么多.其实除了递归函数有一点难运用,其他的都是和记用法一样.
话不多说.进入今日主题
一.三元表达式
概念:其实三元表达式就是一个对流程控制if,else的优化.
语法:一元 if 二元 else 三元
其中一元
指的是当if后面的条件,成立的情况下,返回的值.
二元
值的就是判断的条件
三元
值的就是当if后面的条件,不成立的情况下,返回的值
返回的是一元
或者三元
的值
例子:比较俩个数字的大小,并返回大的数字
方法1:用函数
def max2(x,y):
if x > y:
return x
else:
return y
print(max2(111,432)) # 432
方式2:用三元表达式
x = 111
y = 432
res = x if x > y else y
print(res) # 432
即三元表达式就一个结论:# 即if中间的为判断条件,左边的为条件成立返回的值,右边为条件不成立返回的值
res1 = 'yes' if x > y else 'no'
接下来就是一些生成式
生成式
列表生成式
概念:在列表内做一些表达式操作,将返回的值添加到一个新列表
语法:[i for i in range(10) if i < 6]
- 中间为for循环执行的代码体
- if 后面表示的是循环体内的一个if分支,一个条件判断,可选参数
- 最前面的i表示的是要返回的值
案例:将0-9添加到一个列表中
方式1:平常的代码
l = []
for i in range(10):
l.append(i)
print(l)
方式2:列表生成式
l1 = [i for i in range(10)]
print(l1)
案例2:将0-9中大于6的添加到一个列表中
方式1:平常的代码
l2 = []
for i in range(10):
if i < 6:
l2.append(l2)
方式2:列表生成式,如何优雅的取代了for
和if
l3 = [i for i in range(10) if i < 6]
print(l3)
案例3:将列表中的元素都加上'_sb'
方式1:平常的代码
names = ['song', 'liu', 'xiong']
names1 = []
for name in names:
name = name + '_sb'
names1.append(name)
print(names1)
方式2:列表生成式
l4 = [name+"_sb" for name in names]
print(l4)
案例4:将列表中带_sb的名字打印,用列表生成式实现
names2 = ['jkey', 'liu_sb', 'song_sb']
names4 = [name for name in names2 if name.endswith("_sb")]
print(names4)
其实,你可以发现,列表生成式其实就是一个精简代码的一个内置方法.一般都是搭配for 循环使用
那列表有生成器,其他的字典,元组,和集合呢??
对他们都有对应的生成式.值得一提的是python没有元组表达式因为元组就是一个不可变的列表.
如果你想要进行表达式 ,可以使用 list 和 tuple 之间的转换
所以()
在外面python就有了另外一层表达式的意思,即我们的生成器表达式
那一步步来.让我们看看字典生成器怎么用
字典生成式案例:
dict1 = {f'k_{str(i)}': i**2 for i in range(10) if i > 3}
print(dict1) # {'k_4': 16, 'k_5': 25, 'k_6': 36, 'k_7': 49, 'k_8': 64, 'k_9': 81}
还有集合生成器的使用
集合生成器案例:
set1 = {i for i in range(10) if i > 3}
print(set1) # {4, 5, 6, 7, 8, 9}
和列表生成器的玩法差不多,所以大家看一下案例,知道怎么使用就可以了
还是就是我们的生成器表达式
生成器表达式(==>生成器生成式)
也放俩个案例,来帮助大家理解吧
案例1:生成器表达式的简单使用
res = (i for i in range(3)) # 这里的()表示的就是一个生成器表达式
print(res) # 返回的类型是 生成器对象,即自定义的迭代器对象<generator object <genexpr> at 0x0000029056119CC8>
# 那下面就可以使用next方法去遍历取值了
print(next(res)) # 0
print(next(res)) # 1
print(next(res)) # 2
print(next(res)) # StopIteration
案例2:统计一个文件的字符长度
with open('a.txt', mode='rt', encoding='utf-8') as f:
# 方法1:之间将其全部读到内存.再计算其文件的长度
res = f.read()
print(len(res))
# 方法2:用for循环一行一行的计算长度,并累加
res = 0
for line in f:
res += len(line)
# 方法3:用列表生成器进行上面的方式2操作
res1 = sum([len(line) for line in f])
# 进一步,用生成器
res2 = sum((len(line) for line in f))
# 俩层括号可以简化成一个,即
res3 = sum(len(line) for line in f)
那我们用生成器的一个好处是撒?是的那就是昨天讲的,节省内存空间,即这里因为是一个迭代器对象,所以它不会像列表(可迭代对象)一样,放的是一个个的值在内存中,而是一个可以产生值的对象,来一个就产生一个,要下一个值,上一个就以及在内存中释放了.
是不是发现这些生成式很简单,只要记下怎么使用就好了.
那接下来就来一个带点难度的
递归函数
概念:在调用一个函数的内部又调用自己,所以递归调用的本质就是一个循环的过程
什么意思??循环??那我用while循环和for循环不可嘛
别着急
我们先来看看,递归函数长撒样
def func():
print("func")
func()
func()
上面就是一个简单的递归函数,即在函数体内部执行函数本身.
我们去执行看看
返回结果:
func
func
....
RecursionError: maximum recursion depth exceeded while calling a Python object
欸,它会报错是不是
那是因为我们只是让它执行了函数体本身,但是并没有结束条件啊,所以就相当于是一个死循环了.
而为何这里会报错呢?
是因为python以及为我们考虑到了,你可能会有一个死循环的函数.所以它给我们内部定义了一个可以循环的最大上限次数,一般为1000次.1000次过后则抛异常.为的就是优化内存
可以通过sys模块下的getrecursionlimit,查看最大循环次数
也可以通过setrecursionlimit来重新设置最大循环次数
即下方代码
import sys
sys.setrecursionlimit(2000) # 设置成最大次数为2000
print(sys.getrecursionlimit()) # 查看最大循环的次数(默认为1000)
所以我们递归函数有一个大前提:递归调用一定要在某一层结束
递归其实是有俩个阶段的
即回溯和递推
1、回溯:向下一层一层挖井
2、递推:向上一层一层返回
下面来俩个案例.方便大家理解
现在我有一个需求:
我去问一个人的年龄,第一个人说我的年龄是比第二个人大10岁,然后我们又去问第二个人,第二个人说:我的年龄比第三个人的年龄大10岁,然后我们又去问第三个人,第三个人说.我的年龄比第四个人的大10岁....,n次后,第n个人说我的年龄是18岁,算出第一个人的年龄
现在我们反向思维想一下,代码如下
age(n) = age(n-1) + 10
....
age(5) = age(4) + 10
age(4) = age(3) + 10
age(3) = age(2) + 10
age(2) = age(1) + 10
age(1) = 18
欸,我们看到这不就是一个循环的过程嘛?
我们是不是可以用递归函数试试?
说了就干
def age(n): # n 表示有n个人
if n == 1: # 即是我们告诉了实际年龄的那个人
return 18
return age(n-1) + 10 # 这里表示执行的循环关系
我们调用试试
res = age(5)
print(res) # 58
输出58 是不是没问题
如果这个你没理解,没关系,我们还有案例
当然这个案例,就更好的说明了递归函数使用的恰到好处
案例需求: 一个列表 nums = [1,[2,[3,[4,[5,[6,[7,]]]]]]],我们取出列表的每一个值.
我靠.这不是玩我们嘛?谁没事套这么多列表,是的,我就是玩你.来试着实现吧.
我想我们大家一开始都会想到我们熟悉的for循环,对 for 循环是可以取出来.
for循环代码
for num in l:
if type(num) is list:
for num1 in num:
if type(num) is list:
for num2 in num1:
...
else:
print(num)
我靠,这什么玩意?有几个列表我for几次???
明显这不行.
我们看看根本,我们是一个个拿到新的列表,每次都对新列表做一样的操作,但里面执行的明明是一样的,那个新列表来自的就是上一个的列表中的第二个值.
所以这时候我们想到了我们的递归函数.
代码示例:
nums = [1,[2,[3,[4,[5,[6,[7,]]]]]]]
def get(l):
for num in l:
if type(num) is list:
get(num) # 让他每一次来的都是一个新列表,这不久满足了我们的要求嘛
else:
print(num)
get(nums) # 1,2,3,4,5,6,7
递归函数还有一个用处
我们看看下面一个列表
nums = [-3,1,3,7,13,23,37,43,57,63,77,91,103]
我要看看一个数在不在这个列表里面
很简单嘛
find_num = 65
for num in nums:
if nm == find_num:
print('you got it')
break
else:
print('no exitc')
但你想想如果你查找的值在列表的前面还好,就遍历了,几次,如果是最后面呢?我们是不是要遍历很多次?而且前面的都是无效的遍历.
这时候,用我们的递归函数就有一个好处了.
我们可以拿到中间的那个值,然后再判断,要查找的这个数,和中间这个数比较,大了的话,我就再在中间这个数的右边查找,小了的话,就走这个数的左边查找.依次类推,是不是就很大程度上加快了查询,这就是我们传说中的二分法
来我们用代码实现:
二分法
nums = [-3,1,3,7,13,23,37,43,57,63,77,91,103]
find_num = 64
def find(nums,find_num):
print(nums)
if len(nums) == 0: # 当这个列表被分为空了还没找到,那说明这个要查找的数不在这个列表中
print("not exists")
return # 就不会往下执行了,解决了如果列表为空,还根据索引1取值的异常
mid_index = len(nums) // 2 # 拿到列表的中间索引
if find_num > nums[mid_index]:
# in the right
find(nums[mid_index+1:],find_num)
elif find_num < nums[mid_index]:
# in the left
find(nums[:mid_index], find_num)
else:
print('you got it')
find(nums,find_num)
这个递归函数是不是很有意思.
接下来,还有更有意思的.
我们之前用的函数是不是都带一个名称的.例如 def func():pass 其中的func就是这个函数的名字.那我们是不是也可以有没有名称的函数呢?是的,它就是我们的匿名函数
匿名函数 lambda
概念:一个没有名字的函数,功能却和函数一样
语法: lambda x,y:x + 2 * y
- lambda 后面的 x,y 表示的是匿名函数的参数,
:
后面的表示的是函数的返回值
特点:用于临时用一次的
使用场景:一般搭配内置的函数使用.
下面是匿名函数的俩种之间使用的案例:
方法1:看完就忘(因为这样用我还不如用有名函数)
d = lambda x,y:x + 2*y
print(d) # <function <lambda> at 0x000001CA0CBB7F78>
res = d(1,2)
print(res) # 5
方法2:可以这样用,但可读性差
res = (lambda x,y:x + 2 * y)(1,2)
print(res) # 5
所以还是之间来和我们的新朋友,几个内置函数一起来玩吧
那就边写案例,边介绍吧
案例1:下方有一个员工工资字典,我们根据工资的多少,分别将工资最高的和最低的员工姓名,以及给他们
按照多到少,少到多的员工信息
salaries = {
"egon": 3000,
"tom": 10000,
'zxx': 1000
}
取出最高的,这里先介绍一下,max()这个内置函数的作用
max函数
功能:返回一个可迭代的对象的最大值
语法: max(iteration, [key=func])
- 其中iteration表示的是可迭代对象
- key=func,可选参数,表示按照什么依旧来返回最大值
- func为一个函数内存地址
来加到我们的案例中
def func(k):
return salaries[k] # 依据是字典的value即薪资
print(max(salaries, key=func)) # 返回的是tom
我们再将函数变为匿名函数试试
print(max(salaries, key=lambda k: salaries[k])) # 是等同于 print(max(salaries, key=func))
ok,完成了需求1,返回最大薪资的员工姓名
需求2:返回薪资最低的员工姓名
先介绍min函数
和max函数的使用方法一样,但是它的功能是返回一个可迭代的对象的最小值
语法: max(iteration, [key=func])
- 其中iteration表示的是可迭代对象
- key=func,可选参数,表示按照什么依旧来返回最大值
- func为一个函数内存地址
加到我们案例中
print(min(salaries, key=lambda k: salaries[k]))
需求2也满足了
需求3和需求4,返回他们的顺序,即从低到高和从高到低
函数sorted
功能:返回一个排序后的可迭代对象
语法:max(iteration, [key=func],[reverse=False])
- 其中iteration表示的是可迭代对象
- key=func,可选参数,表示按照什么依旧来返回最大值
- func为一个函数内存地址
- reverse=False,表示默认的以升序排序
放到案例中
print(sorted(salaries, key=lambda k: salaries[k])) # ['zxx', 'egon', 'tom']
print(sorted(salaries, key=lambda k: salaries[k], reverse=True)) # ['tom', 'egon', 'zxx']
ok,需求都完善了
其他内置函数
下面还有一些其他内置函数的玩法.大家可以试着敲敲看看
# map
res = list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # map()返回的是 <map object at 0x00000227FF6FEBC8>
print(res)
names = ["lxx", 'hxx', "wxx", 'lili']
# 规则
# "lxx_sb","hxx_sb"
l = (name + "_sb" for name in names)
res = map(lambda name:name + "_sb",names)
print(list(res))
# filter # 过滤出某个元素
names = ["lxx_sb",'egon',"wxx_sb",'lili_sb']
print([name for name in names if name.endswith('sb')])
res = filter(lambda name:name.endswith('sb'),names)
print(list(res))
# reduce # 拼接元素
from functools import reduce
res = reduce(lambda x,y:x+y,[1,2,3])
res = reduce(lambda x,y:x+y,["aa","bb","cc"])
print(res)
res = pow(10,2,3) # 等同于 10 ** 2 % 3
print(res)
最后给大家来介绍一下什么是面向过程编程
面向过程编程:
核心是"过程"二字,过程就是解决问题的步骤.即先干啥,再干啥,后干啥
所以基于该思想编写程序就好比设计一条一条的流水线
优点:复杂的问题流程化,将一个大问题变成了一个个的小问题,进而就实现了简单化
缺点:牵一发而动全身,扩展性差
总结:
1.三元表达式 xxx if xxx else xxx
第一个xxx 为第二个xxx成立时返回的结果,第三个xxx表示的是第二个xxx不成立时返回的结果
2.生成式:[i for i in range(10) if i > 5] 这样一个模板实例
3.递归函数:在函数内调用函数本身,一定要有结束条件,才有意义.
4.匿名函数:lambda , lambda x,y:x+y
,:
前的为参数,后面的为返回值
5.一些内置方法
max()返回最大值
min()返回最小值
sorted()默认升序返回,当参数reverse=True时,为降序返回
以上内置都有key=func的参数可选 表示的比较的依据,func为一个函数内存地址
map() 映射,改变可迭代对象的值
filter() 过滤
reduce() 拼接元素