匿名函数语法要求
没有名字可以定义出一个函数
必须有lambda
python借助lambda表达式构建匿名函数,要求必须有lambda关键字
lambda关键字 +参数列表 (不加括号)冒号作为分隔符,接着后面写表达式
lambda表达式,也叫匿名函数,有一个参数x,函数体是x2 **
这样就可以调用
写高阶函数必须用到lambda表达式
这两个其实是一样的
上下两个是等价的,foo都是作为标识符
匿名函数使用lambda关键字来定义
参数列表不需要小括号
冒号是用来分割参数列表和表达式的
不需要使用return,表达式的值,就是匿名函数返回值
lambda表达式只能写在一行上,被称为单行函数
都是非常简单的函数用lambda表达式
不管函数怎么调用就恒定一个return值
x,y=3默认值,:函数表达式部分
这是用y=keyword-only参数
这是生成器表达式,后面时传参,解构变成01,2,3,4被*args收集变成元组,被xfor x in args迭代,就是生成器表达式
最后return出来时生成器对象
生成器表达式生成的生成器对象
返回列表,里面+1,原来0123,4,现在是1-5
返回的是set
in后面是要跟可迭代对象
range(5)=01,2,3,4传给 * args 组成元组01234,生成的参数扔给map函数,map函数是一个个处理,map处理以后一个个传入lambda表达式,把元素一个个+1
01234+1 = 12345,也是可迭代对象,塞入到里面去,得到列表{1,2,3,4,5】
map是生成类似一个迭代器的东西,是生成一个惰性求值的对象、、
是一个map得到的可迭代对象,扔给in,in就开始遍历,map是一个惰性求值的函数
这是凑成一个元组返回了,这个一个个元组凑成x进行return
生成[(1),()]
map函数的返回都回加1
\
这是map的两个参数,第1参数,第2参数
不要在后面用等号,不允许,出现赋值表达式
冒号后面属于lambda表达式的部分不能出现等号
之前是,用生成器表达式来得到一个生成器对象,但是表达式的能力比较有限,复杂的是完成不了,需要借助于语句
,能否用函数来得到生成器对象
generator生成器
生成器指的是生成器对象,可以由生成器表达式得到,也可以使用yield关键字得到一个生成器函数,调用这个函数得到一个生成器对象
生成器函数:
必须要求函数体由yield的语句
这个函数调用回返回一个生成器对象,生成器对象是一个可迭代对象,也是迭代器,可以用next方法,是惰性求值,什么时候用就给你算
生成器表达式可以得到一个生成器对象,就可以用next方法,拨一下做一下
有了yield就不是普通函数,是生成器函数,如何得到生成器对象,必须进行调用
仿照来写生成器函数
生成器函数定义也是个函数,要得到生成器函数对象,就需要对函数进行调用
这个函数返回的是一个生成器对象
不管是生成器表达式还是生成器函数得到的,都是生成器对象,这个类型没变,生成器迭代完后不可回头,在拿一个新的生成器对象继续迭代、
for循环可以判断边界,再次迭代是否可以,不可以
可以加缺省值来解决
生成器函数是函数,(不跟其他函数一样,调用返回具体的值),而是返回一个还没有求过任何值的生成器对象,调用这个对象,拨一下转一下
x返回的类型就是生成器了
*两个 *是看不到的,因为在前面已经走完 **
在一个生成器函数中,既可以用yield又可以return
现在g是一个生成器对象,调用了生成器函数,拨一下转一下
再拨一下执行了这一句
对于next函数来讲,再生成器对象找下一个yield,碰到yield,把后面的值返回去,然后自己停止
(函数没有return,但是竟然停了)
如果把这个循环做一次次next
for循环再迭代过程中替你一次次next
拿到4之后
再next试试,进不去循环,就return none回来,告诉你已经到头了,就会抛出异常
只不过for循环在替你做next
第一次,拨到yield1停了,第二次,到yield2停
line3打印了,但是后面的3没有拿到
return返回的值,next函数是不会给你的
next函数碰到return,告诉你没了,到头了,抛出个异常
对于一个生成器来讲,其实是可以用生成器表达式或者生成器函数来构造的,构造可以写多个或者一个yield语句,使用循环,看似是一个yield其实是多个yield
如果没有循环,可以写多个yield
生成器函数调用之后会产生一个生成器对象,你不用next,就没返回值
碰到yield的时候函数会暂停,会把yield后面跟的值拿出来交给next函数来返回,碰到return抛出异常,因为已经到头了
没有显示的语句相当于return none,相当于结束,就会抛出异常
生成器函数必须包含yield语句,调用生成器函数,返回生成器对象,但是生成器对象是懒惰的,不是立即求值,不用next根本不会返回任何数据,哪怕里面有错误,不会报给你,因为没有语法错误,因为有语法错误是构建不出来的
next在执行生成器的时候,会从函数当前位置或者上一次的位置,向后执行,当碰到yield语句的时候,会返回yield后面的值,并且暂停当前函数使用
执行完停止函数,再继续执行
如果再没有yield语句可以执行,就会抛出异常
c是个迭代器
c =counter()调用这个函数,会返回一个生成器对象、生成器对象扔到inc里去
next会拨一下走一下
这样就永远不会到头,死循环
第一次是1
第二次是1
因为下面每次都重置了,inc()
后面加括号,肯定是个可调用对象
c=counter()
next(c)
形成了闭包,内层函数引用到了外层的函数
再多运行几次
调用相当于临时拨了生成器对象一下,一直在上一次的基础上走一趟
自始至终用的都是同一内存对象
把三个函数作为嵌套函数来完成一个功能
再进行改造一下
是等价的
嵌套函数在一起可以完成比较复杂的功能
斐波那契数列的生成器版本,首先先写循环,循环写好封装成函数,然后在里面,yield出来值即可
a,b=0,1起始值
斐波那契数列的生成器版本做好了
要考斐波那契数列,要么递归,要么生成器,必须记住
yield会函数暂停执行
生成器函数能帮助外面写更加复杂的生成器对象,生成器表达式可以生成一个比较简单的生成器对象,更多的是使用生成器函数来创建一个生成器对象出来
生成器最大的好处是可以惰性求值
(列表解析式只要执行,就会把所有元素填到列表里了,列表立即返回)但是生成器是要一个给一个,不需要立即把所有值给你,这是惰性求值的好处,减少负担
yield可以暂停函数执行,这功能非常强大
**除了进程和线程,python,和ruby还提出了协程coroutine,这个用的好,你的效率会很高,而且轻量
是生成器的高级语法
是在用户空间进行调度的实现,线程无非就是执行个函数,只不过是在不太的线程中去执行,但是线程太多事有问题的,而且线程的切换是有问题的,尤其在python中,线程不见得效率很高。
这时候就提出了协程 coroutine
跟生成器有关系,就因为yield语句
python3提出了asyncio就是协程实现的,已经加入到标准库
**
在用户空间调度函数是什么概念,操作系统有内核空间和用户空间,这个协程不在操作系统和内核空间进行切换调度,
在用户空间就直接完成切换
(比如a线程执行完执行b线程,a进程执行完执行b线程,这两种操作都是要进内核空间的,但是协程不用再内核空间中去完成)
协程调度器实现思路,有两个函数,都有yield语句
第一个程序执行完执行下一个
改造一下
定义两个生成器函数
再执行一次
相当于在外面加for循环
再次执行
跟线程差不多,线程做事情也是调度的,这是一种调度,轮询,最简单的策略,也是最常用的策略
线程进程也是需要调度的,但是都不是在用户空间完成的
协程是在用户空间完成的,协程的效率非常高,多线程的效率都未必有协程高
如果愿意可以自己写调度策略,执行a再执行b
非抢占式的,因为执行到yield之后才能暂停,后面才能进行调度
yield语句会暂停当前函数执行,让出控制权,交给外面调度的
有yield就算生成器函数
是3.3出现的新语法
函数定义跳过,inc(10)传进来,
counter(10)生成器对象,可以迭代,把里面的值迭代,写成yield from
递归,函数的调用过程,压栈,局部变量也压栈,函数调用完弹出,会把返回值给你,函数压栈理解之后,才能理解递归,压栈是由次数限制的,栈空间有限制的
每压一次函数,局部变量都有一大堆东西,栈空间很快就用满,是有限的,所以python是做了保护了
递归很容易自己调用自己,几千下过去了,cpython设定的保护是1000,要注意递归的深度问题,
自己调自己,或者间接调用自己,间接调用是非常危险的
注意斐波那契数列的递归写法这是最简单的,一定会问
匿名函数lambda比较简单,lambda表达式可以写复杂
生成器一定要注意惰性求值,yield语句本身可以暂停函数执行并且让出控制权,这个有了就可以用协程,生成器函数只要带yield就算,调用之后才会返回生成器对象
abcd,123ad,求公共字(ab)这样的,字符串是有连续的