概念
可以让函数生成一个序列(生成器对象),而不是一个值
一个生成器能暂停执行并返回一个中间的结果 —— 这就是 yield 语句的功能 : 返回一个中间值给调用者并暂停执行,交出控制权。
调用next()生成器函数一直运行到下一条yield语句为止(到yield暂时停止,下次从这开始)
yield n 实际是一个函数表达式,yield + 参数
m = yield n m不等于n //调用next(),next返回参数n m的值一直为None,send(None)
next(), send()的返回值都是yield 后面的参数 n
send()是可以传递(yield表达式)的值进去--表达式的结果
m = yield n //m的值就为send(x)的参数x
而next()是发送一个None给(yield n)表达式 send(None)
换句话说,就是send可以强行修改上一个yield表达式值
x = yield这个表达式的计算过程是先计算等号右边的内容,然后在进行赋值,所以当激活生成器后,程序会停在yield这里,但并没有给x赋值。
需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有yield语句来接收这个值。
启动生成器必须使用next()语句或是send(None)启动生成器,不能使用send发送一个非None的值
不管数据如何流动, yield 都是一种流程控制工具,使用它可以实现协作式多任务:协程可以把控制器让步给中心调度程序,从而激活其他的协程
关于调用next(...)函数这一步通常称为”预激(prime)“协程,即让协程向前执行到第一个yield表达式,准备好作为活跃的协程使用
协程
同步式的编程,通过生成器函数来实现类似异步的操作
协程库的运用,和内部实现机制
多线程,多进程 系统内部处理调用
协程: 人为的代码操作顺序流,在单线程中实现异步的感觉,所有说是在单线程中实现的,没有多线程和多进程实现会碰到的麻烦
只会有一点协程切换时的开销(如上下文环境的保存)
相当于一个函数中,调用非常多的子函数,协作式的完成一个主函数,不是抢占式(由系统判定和调用)
再加上一个事件驱动
无法利用多核,最好加上多进程,来提高运行
协程在运行过程中有四个状态:
GEN_CREATE:等待开始执行
GEN_RUNNING:解释器正在执行,这个状态一般看不到
GEN_SUSPENDED:在yield表达式处暂停
GEN_CLOSED:执行结束
预激协程的装饰器:预防 协程没有预先激发报错
关于预激,在使用yield from句法调用协程的时候,会自动预激活,这样其实与我们上面定义的coroutine装饰器是不兼容的,在python3.4里面的asyncio.coroutine装饰器不会预激协程,因此兼容yield from
yield from x 表达式对 x 对象所做的第一件事是,调用 iter(x),从中获取迭代器,因此, x 可以是任何可迭代的对象,这只是 yield from 最基础的用法==。
yield from 结构会自动捕获StopIteration异常,这种处理方式与for循环处理StopIteration异常的方式一样,循环机制使我们更容易理解处理异常,对于yield from来说,解释器不仅会捕获StopIteration异常,还会把value属性的值变成yield from表达式的值
eg.1
def gen():
for c in "AB":
yield c
for i in range(1,3):
yield i
print(list(gen()))
def gen2():
yield from "AB"
yield from range(1,3)
print(list(gen2()))
协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。
每个协程表示一个执行单元,有自己的本地数据,与其它协程共享全局数据和其它资源。
协作式的任务,是要用户自己来负责任务的让出的。如果一个任务不主动让出,其他任务就不会得到调度
import inspect//检查类型库
线程有两个必须要处理的问题:
一是碰着阻塞式I\O会导致整个进程被挂起;
二是由于缺乏时钟阻塞,进程需要自己拥有调度线程的能力。
才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),
然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行
必须在只有一个单线程里实现并发
修改共享数据不需加锁
用户程序里自己保存多个控制流的上下文栈
一个协程遇到IO操作自动切换到其它协程
yield from
yield from iterator
相当于
for x in iterator:
yield x
yield from用于重构生成器
yield from的作用还体现可以像一个管道一样将send信息传递给内层协程,并且处理好了各种异常情况
发展历程
1. 最初的生成器变形yield/send
2. 引入@asyncio.coroutine和yield from
3. 在最近的Python3.5版本中引入async/await关键字
总结
生成器是一种迭代器
python的迭代器是包含__next__ 方法的任何对象
迭代对象是任何定义了__iter__ 方法的对象。可迭代对象的__iter__ 方法负责返回一个迭代器
生成器:减少内存的占用,只关注当前值,而不用创建一个数组保留所有的数
内置函数使用生成器原理实现的机制
生成器交互 send()
send(),next() 返回的参数
yield xxx 表达式的返回值
yield from
事件循环,注册函数,监控FUTURE对象(包含协程的状态和result)
实现异步:减少I/O操作的等待
事件循环在这里将来自套接字(socket)的 I/O 已经准备好读和/或写作为“当A发生时”(通过selectors模块)
并发:一个CPU核,干很多事。感觉像是同时做很多事
并行:两个CPU核,干两件事
网友评论