Python函数02

📅 2026/7/18 16:17:29
Python函数02
一、闭包1.函数名的本质就是变量名可以被赋值也可以给别的变量赋值做参数传递做返回值闭包其实就是在内层函数中使用了外层函数的变量。defouter():a10definner():print(a)returninner我们可以使用一个变量去接收outer函数得到的结果是inner函数aouter()由于函数inner的执行时间是在outer外部. 这就决定了inner执行的时间我们是不确定的. 而变量a是一个局部变量. 正常情况下执行玩儿outer, 它就没有意义了. 但是, 此时由于inner函数执行时间的不确定. 又必须保证inner能正常执行. python就规定. 闭包函数中使用的变量会常驻于内存. 而且. 在outer外部. 是无法改变这个值的. 故称: 闭包. 目的有两个: 其一是不许外面改变这个变量. 其二是让这个变量常驻于内存.闭包的应用:装饰器二、装饰器就有一点像Java的切面那一种感觉比如记录日志这样呀等等# 装饰器基本就是下面的这样下面的这个属于是通用的# wrapper函数接收对应的要被执行的函数然后inner中接收对应函数的参数以及执行对应的函数并返回对应执行函数的结果defwrapper(fn):definner(*args,**kwargs): 在执行fn之前 retfn(*args,**kwargs) 在执行fn之后 returnret# 把fn执行之后的结果返回returninner三、迭代器迭代器作用就是统一容器类型循环遍历的标准。我们熟悉的一些可迭代对象有str, list, tuple, dict, set在打印结果中. 寻找__iter__ 如果能找到. 那么这个类的对象就是一个可迭代对象.例子shelloprint(__iter__indir(s))# 输出结果 - True然后可以试试其他的list, tuple, dict, set这些都是True.查看一个对象是否是可迭代对象的第一种办法. 我们还可以通过isinstence()函数来查看一个对象是什么类型的l[1,2,3]l_iterl.__iter__()fromcollectionsimportIterablefromcollectionsimportIteratorprint(isinstance(l,Iterable))#Trueprint(isinstance(l,Iterator))#Falseprint(isinstance(l_iter,Iterator))#Trueprint(isinstance(l_iter,Iterable))#True我们可以确定. 如果对象中有__iter__函数. 那么我们认为这个对象遵守了可迭代协议. 就可以获取到相应的迭代器. 这里的__iter__是帮助我们获取到对象的迭代器. 我们使用迭代器中的__next__()来获取到一个迭代器中的元素shellocs.__iter__()print(c.__next__())# hprint(c.__next__())# eprint(c.__next__())# lprint(c.__next__())# lprint(c.__next__())# oprint(c.__next__())# StopIteration使用 while 循环 迭代器模拟 for 循环lst[1,2,3]lst_iterlst.__iter__()whileTrue:try:nextlst_iter.__next__()print(next)exceptStopIteration:break四、生成器生成器的本质就是迭代器在Python中有俩种方式来获取生成器1.通过生成器函数2.通过生成器表达式来实现生成器deffunc():print(111)return222retfunc()print(ret)结果:111222将 return 替换成 yield 就是生成器deffunc():print(111)yield222retfunc()print(ret)结果:generatorobjectfunc at0x10567ff68由于函数中存在了yield. 那么这个函数就是一个生成器函数. 这个时候. 我们再执行这个函数的时候. 就不再是函数的执行了想要获取到具体的直接执行__next__()来执行以下生成器.def func(): print(111) yield 222 ret func() print(ret.__next__()) 结果 111 222那么我们可以看到, yield和return的效果是一样的. 有什么区别呢? yield是分段来执行一个函数. return呢? 直接停止执行函数.deffunc():print(111)yield222print(333)yield444generfunc()retgener.__next__()print(ret)ret2gener.__next__()print(ret2)ret3gener.__next__()# 最后一个yield执行完毕. 再次__next__()程序报错, 也就是说. 和return无关了.print(ret3)接下来我们来看send方法, send和__next__()一样都可以让生成器执行到下一个yield.geneat()# 获取生成器ret1gen.__next__()print(ret1)ret2gen.send(胡辣汤)print(ret2)ret3gen.send(狗粮)print(ret3)ret4gen.send(猫粮)print(ret4)结果 我吃什么啊 馒头 a胡辣汤 大饼 b狗粮 韭菜盒子 c猫粮 GAME OVERsend和__next__()区别:send和next()都是让生成器向下走一次send可以给上一个yield的位置传递值, 不能给最后一个yield发送值. 在第一次执行生成器代码的时候不能使用send()五、列式推导式-生成器表达式以及其他推导式5.1 列式推导式我们来看下面的这段代码给出一个列表通过循环像列表中添加数据。lst[]foriinrange(10):lst.append(i)print(lst)# 输出结果# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]替换成列表推导式为lst[iforiinrange(10)]print(lst)# 输出结果# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]列式推导式是通过一行来构建想要的列表的。列式推导式的常用写法为:[结果 for 变量 in 可迭代对象]我们还可以通过对于列表进行数据的筛选筛选模式:[结果 for 变量 in 可迭代对象 if 条件]例子# 获取 1-10 内所有的偶数lst[iforiinrange(1,11)ifi%20]print(lst)# 输出结果# [2, 4, 6, 8, 10]5.2 生成表达式生成表达式和列式推导式的语法基本一致只是把[]替换为()如下所示:gen(iforiinrange(10))# 打印出来的结果为一个生成器print(gen)# 输出结果# generator object genexpr at 0x0000020A382D0EE0打印的结果就是一个生成器我们可以使用for循环来循环这个生成器:salary(我实习期间的工资为 %s k%iforiinrange(10))# 循环生成器输出具体的结果foriinsalary:print(i)# 输出结果# 我实习期间的工资为 0 k# 我实习期间的工资为 1 k# 我实习期间的工资为 2 k# 我实习期间的工资为 3 k# 我实习期间的工资为 4 k# 我实习期间的工资为 5 k# 我实习期间的工资为 6 k# 我实习期间的工资为 7 k# 我实习期间的工资为 8 k# 我实习期间的工资为 9 k生成表达式也是可以进行筛选的# 生成器进行对应的条件筛选# 找到 0-9 中能够被3整除的数字gen(iforiinrange(10)ifi%30)foriingen:print(i)# 输出结果# 0# 3# 6# 9# 寻找名字中带有两个e的人的名字names[[Tom,Billy,Jefferson,Andrew,Wesley,Steven,Joe],[Alice,Jill,Ana,Wendy,Jennifer,Sherry,Eva]]gen(namefornameListinnamesfornameinnameListifname.count(e)2)foriingen:print(i)# 输出结果:# Jefferson# Wesley# Steven# Jennifer生成器表达式和列式推导式的区别:​ 1.列式推导式比较的耗内存一次性加载出来。生成器表达式几乎是不占用内存的使用的时候才进行分配和使用内存。​ 2.得到的值是不一样的。列式推导式得到的是一个列表生成器表达式得到的是一个生成器生成器的惰性机制生成器只有在访问的时候才会进行取值。简单来说就是你找他要他才给你。不找他要不会给你。5.3 字典推导式字典推导式语法:{新键:新值for变量in可迭代对象}例子:# 把字典中的key和value互换dic{a:1,b:2}new_dic{dic[key]:keyforkeyindic}print(new_dic)# 输出结果# {1: a, 2: b}# 在以下list中. 从lst1中获取的数据和lst2中相对应的位置的数据组成一个新字典lst1[jay,jj,sylar]lst2[周杰伦,林俊杰,jolin]dic{lst1[key]:lst2[key]forkeyinrange(len(lst1))}print(dic)# 输出结果# {jay: 周杰伦, jj: 林俊杰, sylar: jolin}5.4 集合推导式集合推导式可以帮我们直接生成一个集合. 集合的特点: 无序, 不重复. 所以集合推导式自带去重功能例子lst[1,-1,8,-8,12]# 绝对值去重s{abs(i)foriinlst}print(s)# 输出结果# {8, 1, 12}在python3中提供了一种可以直接把可迭代对象中的每一个数据作为生成器的结果进行返回例子defdemo():lst[demo1,demo2,demo3,demo4]yieldfromlst demodemo()fordindemo:print(d)输出结果demo1 demo2 demo3 demo4六、匿名函数为了解决一些简单的需求而设计的一句话函数。语法如下函数名lambda参数:返回值lambda 表示的是匿名函数不需要使用def声明一句话就可以声明出一个函数。如下所示deffunc(n):returnn**nprint(func(2))flambdan:n**nprint(f(2))注意函数的参数可以有多个多个参数之间使用逗号分隔开匿名函数不管有多复杂只能写一行且逻辑结束后直接返回数据返回值和正常的函数一样可以是任意的数据类型匿名函数并不是说一定没有名字这些前面的变量就是一个函数名说他是匿名函数的原因是通过__name__查看的时候没有名字统一为lambda。在调用的时候和其他的函数没有什么区别。七、内置函数7.1 sorted函数排序函数语法sorted(Iterable keyNone reverseFlase)Iterable-代表的是可迭代的对象 key-代表的是按照一定的排序规则。在sorted内部将可迭代的对象中的每一个元素传递给函数的参数。然后根据函数运算的结果进行排序 reverse-是否倒序True倒序False:正序举例如下# sorted 函数lst[1,9,6,3]new_lstsorted(lst)print(f排序前的列表{lst})print(f排序后的列表{new_lst})输出结果 排序前的列表[1,9,6,3]排序后的列表[1,3,6,9]可以和函数组合使用lst[hello,world,yang,zhi]# 可以和函数组合使用(正常函数和lambda函数都是可以的)print(sorted(lst,keylambdax:len(x)))输出结果[zhi,yang,hello,world]7.2 filter 函数筛选函数语法:filter(function,Iterable)function-用来筛选的函数在filter中回自动把 Iterable 中的元素传递给 function,然后根据 function 返回的True或者False来判断是否保留此项数据 Iterable-可迭代对象举例如下# filter 函数lst[1,2,3,4,5,6,7,8,9]# 筛选能被3整除的数,返回的是一个filter对象也就是迭代器new_lstfilter(lambdai:i%30,lst)print(new_lst)print(list(new_lst))输出结果filterobjectat0x000001D1D2C25DB0[3,6,9]7.3 map() 映射函数语法map(function, Iterable) 可以对可迭代对象中的每一个元素进行映射分别取执行function# map 映射函数print(list(map(lambdax:x*x,[1,2,3,4])))# 输出结果[1,4,9,16]# 计算俩个列表相同位置的数据的和# map(function, Iterable) 映射函数lst1[1,2,3,4,5]lst2[2,4,6,8,10]print(list(map(lambdax,y:xy,lst1,lst2)))# 输出结果[3,6,9,12,15]八、三元表达式和递归8.1 三元表达式语法结果1if条件else结果2如果条件为真返回结果1否则返回结果2a1b2print(fa {a}, b {b}, a 和 b 中的较大者为{aifabelseb})输出结果:a1,b2,a 和 b 中的较大者为28.2 递归递归就是自己调用自己需要注意的是递归终止条件。否则就会无限的调用自己。虽然Python设置了递归最大深度1000