协程学习

学习python的多线程与多进程后,再学学协程

可迭代与迭代器

可迭代(Iterable):可直接作用于for循环的变量

迭代器(Iterator): 不但可以作用于for循环,还可以被next()调用

  • 使用insinstance()判断一个变量是可迭代的还是迭代器
1
2
3
4
5
6
7
from collections import Iterable,Iterator

# insinstance: 判断某个变量是否是一个实例

var = [1,2,3,4]
print(isinstance(var, Iterable))
print(isinstance(var, Iterator))

打印结果:

True
False

说明: list是可迭代的,但不是迭代器

  • 可以通过iter()将Iterable转换为Iterator
1
2
3
4
5
6
7
8
9
10
from collections import Iterable,Iterator

var = 'hello world'
print(isinstance(var, Iterable))
print(isinstance(var, Iterator))

var_1 = iter(var)

print(isinstance(var_1, Iterable))
print(isinstance(var_1, Iterator))

运行结果:

True
False
True
True

生成器

生成器(generator: 一边循环一边计算下一个元素的机制/算法)

需要满足三个条件:

  • 每次调用都生产出for循环需要的下一个元素
  • 若达到最后一个,则抛出StopIteration异常
  • 可以被next函数调用

直接使用

直接使用生成器

1
2
3
4
5
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

如果函数中包含yield(类似于return),则这个函数就叫做生成器

使用next调用函数,遇到yield返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
print('使用普通函数')
def func():
print('step 1')
print('step 2')
print('step 3')
return None

func()
# 执行过程为:找到func这个函数定义的地方->从上往下依次运行

print()
print('使用生成器')
def func1():
print('step 1')
yield 1
print('step 2')
yield 2
print('step 3')
yield

g = func1() # 生成一个生成器g

t1 = next(g) # next函数相当于执行下一步,yield返回结果赋值给t1
print(t1)
t2 = next(g)
print(t2)
t3 = next(g)
print(t3)

# 执行过程为: 执行next,从上往下执行,遇到yield则返回结果->再次执行next,从上回yield执行后接着执行

打印结果:

使用普通函数
step 1
step 2
step 3

使用生成器
step 1
1
step 2
2
step 3
None

使用for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 使用生成器实现斐波那契数列

def func(max):
n = 0
a = b = 1
while n < max:
yield a
c = a + b
a = b
b = c
n += 1
return 'Done'

G = func(5)
for i in range(6):
t = next(G)
print(t)

打印结果:

StopIteration                             Traceback (most recent call last)
<ipython-input-18-885eac2b009f> in <module>()
     12 G = func(5)
     13 for i in range(6):
---> 14     t = next(G)
     15     print(t)

StopIteration: Done

发现: 达到最后一个后再执行next,会抛出StopIteration异常,返回的值为return的值


在for循环中使用生成器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def func(max):
n = 0
a = b = 1
while n < max:
yield a
c = a + b
a = b
b = c
n += 1
return 'Done'

G = func(5)
for i in G:
print(i)

打印结果:

1
1
2
3
5

生成器的典型用法就是在for中使用,比较常用的生成器就是range

-> 小结:生成器节省内存,而list比较消耗内存

协程

python3.4引入协程

实现协程较好的包有:asyncio, tornado, gevent

基本使用

协程的实现:

  • yield返回
  • send调用
1
2
3
4
5
6
7
8
9
10
11
def func():
print('start')
x = yield
print('receive x: ', x)

c = func() # 生成协程c
next(c) # 预激
# 让协程执行到第一个yield表达式,准备好作为活跃的协程使用
# 或使用c.send(None)预激

c.send('hello') # 此时yield会收到值并赋给x

运行结果:

start
receive x:  hello
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-1-758b3ae2db03> in <module>()
      6 c = func()
      7 next(c)
----> 8 c.send('hello')

StopIteration: 

协程的状态

  • GEN_CREATE:等待开始执行
  • GEN_RUNNING:解释器正在执行,这个状态一般看不到
  • GEN_SUSPENDED:在yield表达式处暂停
  • GEN_CLOSED:执行结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def func(a):
print('start')
b = yield a # GEN_SUSPENDED
print(a, b) # 5 6
c = yield a+b
print(a, b, c) # 5 6 7
# GEN_CLOSED

c = func(5) # GEN_CREATE
t1 = next(c) # GEN_RUNNING
print(t1) # 5
t2 = c.send(6) # b = 6
print(t2) # 11
t3 = c.send(7) # c = 7

打印结果:

start
5
5 6
11
5 6 7
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-18-d434fb8c4394> in <module>()
     11 t2 = c.send(6)  # b = 6
     12 print(t2)  # 11
---> 13 t3 = c.send(7)  # c = 7
     14 

StopIteration: 

委派生成器

引入yield from:

1
2
3
4
5
6
7
8
9
10
11
12
13
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()))

打印结果是一样的

['A', 'B', 1, 2]
['A', 'B', 1, 2]

yield from x:

表达式对x这个对象所做的第一件事是,调用iter(x),从中获取迭代器,因此x可以是任何可迭代的对象


委派生成器:

  • 包含yield from表达式的生成器函数
  • 委派生成器在yield from表达式处暂停,调用方可直接把数据发给子生成器
  • 子生成器再把值发给调用方
  • 子生成器在最后,解释器会抛出StopIteration,并且把返回值附加到异常对象上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from collections import namedtuple

# 命名元组
ResClass = namedtuple('Res', 'count average')

# 子生成器
def averager():
total = 0.0
count = 0
average = None

while True:
term = yield
# None是哨兵值
if term is None:
break
total += term
count += 1
average = total/count

return ResClass(count, average)


# 委派生成器
def grouper(storages, key):
while True:
storages[key] = yield from averager()


# 客户端代码
def client():
process_data = {
'boys_2': [39.0, 40.8, 43.2],
'boys_1': [1.38, 1.5, 1.32]
}
storages = {}
for k, v in process_data.items():
# 生成协程
c = grouper(storages, k)

# 预激
next(c)

# 传数据
for data in v:
c.send(data)

# 终止
c.send(None)
print(storages)

client()

打印结果:

{'boys_2': Res(count=3, average=41.0), 'boys_1': Res(count=3, average=1.4000000000000001)}

grouper发送的每个值都会经由yield from处理,通过管道传给averager实例。grouper会在yield from表达式处暂停,等待averager实例处理客户端发来的值。averager实例运行完毕后,返回的值会绑定到results[key]上,while 循环会不断创建averager实例,处理更多的值


参考:

https://www.cnblogs.com/zhaof/p/7631851.html