协程
- 参考资料
迭代器
- 可迭代(Iterable):直接作用于for循环的变量
- 迭代器(Iterator):不但可以作用于for循环,还可以被next调用
- 注意:可迭代对象不一定是迭代器。list是典型的可迭代对象,但不是迭代器
#可迭代 l = [i for i in range(10)] #l是可迭代的,但不是迭代器 for idx in l: print(idx) # range是一个迭代器 for i in range(10): print(i)
- 可以通过isinstance来判断
结果:# isinstance案例 # 判断是否可以迭代 for collections import Iterable ll = [1,2,3,4,5] print(isinstance(ll,Iterable)) # 判断是否是迭代器 from collections import Iterator print(isinstance(ll,Iterator))
True False
- 把可迭代对象转换为迭代器:iter函数
结果:# iter函数 from collections import Iterable,Iterator s = "i love aksdjkajaks" print(isinstance(s,Iterable)) print(isinstance(s,Iterator)) # 使用iter函数 s_i = iter(s) print(isinstance(s_i,Iterable)) print(isinstance(s_i,Iterator))
True False True True
- 注意:可迭代对象不一定是迭代器。list是典型的可迭代对象,但不是迭代器
生成器
- generator:一边循环一边计算下一个元素的机制/算法
- 需要满足三个条件:
- 每次调用都生产出for循环需要的下一个元素
- 达到最后一个元素后,报出StopIteration异常
- 可以被next函数调用
- 生成一个生成器
-
直接生成
# 直接使用生成器 # 放在中括号中是列表生成器 l = [x*x for x in range(5)] # 放在小括号中就是生成器 g = (x*x for x in range(5)) print(type(l)) print(type(g))
程序结果:
<class 'list'> <class 'generator'>
-
如果函数中包含yield语句,这个函数就是生成器。此类函数只能使用next调用,遇到yield就返回
# 函数案例,此运行结果较简单 def odd(): print("Step 1") print("Step 2") print("Step 3") return None # 调用函数 odd()
把函数odd()变成生成器调用
# 生成器案例 # 在odd中,yield负责返回 def odd(): print("Step 1") yield 1 print("Step 2") yield 2 print("Step 3") yield 3 # 生成一个生成器 g = odd() one = next(g) print(one) # 第一次调用生成器后,在内部记住了上次运行后的位置,直接从语句print("Step 2")开始 two = next(g) print(two) # 同上 three = next(g) print(three)
-
for循环调用生成器(斐波纳契数列的写法)
# 函数写法 def fib(max): n,a,b = 0,0,1 while n<max: print(b) a,b = b,a+b n += 1 return "Done" fib(6)
结果:
1 1 2 3 5 8 # 注意,这里有return返回的值,只是没有打印出来
# 生成器写法 def fib(max): n,a,b = 0,0,1 while n<max: yield b a,b = b,a+b n += 1 return "Done" g = fib(6) for i in range(7): rst = next(g) print(rst)
这种调用方法一定会报出StopIteration,结果如下:
1 1 2 3 5 8 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-4-b8fd53e94015> in <module>() 10 g = fib(6) 11 for i in range(7): ---> 12 rst = next(g) 13 print(rst) StopIteration: Done
# 上述代码改进 def fib(max): n,a,b = 0,0,1 while n<max: yield b a,b = b,a+b n += 1 return "Done" """ 生成器的典型用法就是放在for中使用 比较常用的典型生成器就是range() """ g = fib(6) # 在for循环中使用生成器 for i in g: print(i)
-
协程
- 历史进程
- 3.4引入协程,用yield实现
- 3.5引入协程语法
- 实现协程比较好的包有asyncio,tornado,gevent
- 定义:协程是为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序
- 从技术的角度讲,协程就是一个你可以暂停执行的函数,或者干脆就把协程理解成生成器
- 实现:
- yield返回
- send调用
- 优点:
- 协程的切换相较于多线程很节约资源
# 协程案例1
def simple_coroutine():
print("-> start")
x = yield
print("-> recived",x)
# 主线程
sc = simple_coroutine()
print(111)
#可以使用sc.send(None),效果是一样的
next(sc) #预激
print(222)
sc.send("hello")
结果:
111
-> start
222
-> recived hello
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-8-756614beb6e0> in <module>()
13
14 print(222)
---> 15 sc.send("hello")
StopIteration:
- 程序执行顺序如图:
- 协程的四个状态
- inspect.getgeneratorstate(…) 函数确定,该函数会返回下述字符串中的一个:
- GEN_CREATED:等待开始执行
- GEN_RUNNING:解释器正在执行
- GEN_SUSPENED:在yield表达式处暂停
- GEN_CLOSE:执行结束
- next 预激(prime)
# 协程案例2
def simple_coroutine():
print("-> start")
b = yield a
print("-> recived",a,b)
c = yield a + b
print("-> recived",a,b,c)
# runc
sc = simple_corcoutine(5)
aa = next(sc)
print(aa)
bb = sc.send(6)
print(bb)
cc = sc.send(7)
print(cc)
代码运行结果:
-> start
5
-> recived 5,6
11
-> recived 5 6 7
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-8-756614beb6e0> in <module>()
16 bb = sc.send(6)
17 print(bb)
---> 18 cc = sc.send(7)
19 print(cc)
20
StopIteration:
程序运行流程
-
协程终止
- 协程中未处理的异常会向上冒泡,传给next函数或send方法的调用方(即触发协程的对象)
- 终止协程的一种方式:发送某个哨符值,让协程推出。内置的None和Ellipsis等常量经常用作哨符值。
-
yield from
- 调用协程为了得到返回值,协程必须正常终止
- 生成器正常终止会发出StopIteration异常,异常对象的value属性保存返回值
- yield from从内部捕获StopIteration异常
def gen(): for c in "AB": yield c # list直接用生成器作为参数 print(list(gen())) def gen_new(): yield from "AB" print(list(gen_new()))
结果为:
['A','B'] ['A','B']
-
委派生成器
- 包含yield from表达式的生成器函数
- 委派生成器在yield from表达式处暂停, 调用方可以直接把数据发给子生成器
- 子生成器再把产出的值发给调用方
- 子生成器在最后,解释器会抛出StopIteration,并且把返回值附加到异常对象上