python百万级并发_Python进阶:百万「并发」基础之异步编程(中篇)

HackPython致力于有趣有价值的编程教学

简介

在上一篇,讨论了阻塞/非阻塞、同步/异步、并发/并行等概念,本节主要来讨论一下生成器、yield以及yield from概念并进行简单的使用。

关键概念

Python中利用了asyncio这个标准库作为异步编程框架,而aysncio以及其他多数协程库内部都大量使用了生成器,所以先从生成器聊起。为什么会是生成器🤔?回想一下生成器的特性,其利用了yield关键字做到了随时暂停以及随时执行的能力,而协程从技术实现角度而言,它的作用其实就是一个可以随时暂停会执行的函数。

生成器

生成器与迭代器关系紧密,😗其实生成器就是迭代器另一种更优雅的实现方式,其利用了yield关键字实现了迭代器的功能,生成器可以迭代式的利用内存空间,让数据在需要使用时才被载入,这减少内存的消耗,其利用yield关键字使用了这个功能,当生成器函数执行过程中遇到yield就会被展厅执行,等下次迭代时再次从暂停处继续执行。

为了让生成器可以实现简单的协程,🤩在Python 2.5 的时候对生成器的能力进行了增强,此时利用yield可以暂停生成器函数的执行返回数据,也可以通过send()方法向生成器发送数据,并且还可以利用throw()向生成器内抛出异常以实现可随时终止生成器的目的。

yield的作用直观如下图:

从图中可看出,在一开始调用simple_coro2()方法时,获得的my_coro2变量并不是具体的值,而是一个生成器对象,此时调用其next()方法进行迭代,next()方法会让生成器函数执行到yield处,到yield后就会会将紧随在其后的变量返回,接着可以利用send()方法将值传递到生成器中,并让暂停的函数继续从暂停处执行😏,next()与send()的不同之处在于next()并不能向生成器内部传递值而send()可以,可以直接使用send(None)来实现next()方法的效果。从图中也可以看出,next()与send()会获得下一个yield返回的值。

顺带一提,for迭代也调用了迭代器中的__next__方法,next()内部也是该方法🤫。

yield from

为了让生成器分成多个子生成器后可以很容易使用next()、send()、throw()等方法,Python3.3中引入了yield from语言🤩,它允许将一个生成器的部分操作委派给另一个生成器。

虽然yield from设计的目的是为了让生成器本身可以委派给子生成器,但yield from可以向任意可迭代对象进行委派操作🤭。

yield from iterable 本质其实就是 for item in iterable: yield item,只是写法更优雅了,简单使用如下

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17In [1]: def gen1():

...: for i in 'abc':

...: yield i

...: for i in range(5):

...: yield i

...:

In [2]: list(gen1())

Out[2]: ['a', 'b', 'c', 0, 1, 2, 3, 4]

In [4]: def gen2():

...: yield from 'abc'

...: yield from range(5)

...:

In [5]: list(gen2())

Out[5]: ['a', 'b', 'c', 0, 1, 2, 3, 4]

上述代码中其实涉及几个概念,其中gen2()方法因为包含了yield from表达式,所以被称为😀委派生成器,而yield from后接着的表达式通常称为😀子生成器,上述代码中的’abc’,range(5)都是子生成器,而滴啊用委派生成器的代码称为😀调用方。

此外,yield from还可以直接将调用方发送的信息直接传递给子生成器,具体可以看下面代码

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

50from collections import namedtuple

Result = namedtuple('Result', 'count average')

# the subgenerator

def averager():

total = 0.0

count = 0

average = None

while True:

term = yield

print('term:', term)

if term is None:

break

total += term

count += 1

average = total / count

return Result(count, average)

# the delegating generator

def grouper(results, key):

while True:

#只有当生成器averager()结束,才会返回结果给results赋值

results[key] = yield from averager()

print('resluts[key]:', results[key])

def report(results):

for key, result in sorted(results.items()):

group, unit = key.split(';')

print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))

def main(data):

results = {}

for key, values in data.items():

group = grouper(results, key)

print(type(group))

next(group)

for value in values:

r = group.send(value)

print('r:',r)

print('value:',value)

group.send(None)

report(results)

data = {

'girls;kg':[40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],

'girls;m':[1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],

'boys;kg':[39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],

'boys;m':[1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],

}

if __name__ == '__main__':

main(data)

在上述代码中,grouper函数是委托生成器😗,averager函数是子生成器😗,而main()函数就是调度者😗。

在main()函数中,首先通过grouper()获得对应的生成器对象,然后调用next()方法进行初步的迭代,此时会执行到averager()的yield处,因为yield后没有跟对应的变量,则yield返回的值为None,该值会有grouper()委托生成器直接传递给main()调度者,观察变量r的打印则可,接着for迭代中使用委托生成器的send()方法,该方法发送的数据会有委托生成器直接传递给子生成器,即averager()函数中term的值,上述代码调度的关系如下图:

从图中看出,😐调度者使用send()方法传递的数据会被委派生成器直接传递给子生成器,而子生成器yield的方法数据也被直接传递会调度者,如果子生成器产生StopIteration异常则表示子生成器已经迭代完了,此时委派生成器会接收到该异常,从而继续执行yield from整个表达式后的其他表达式,这里grouper()函数中yield from执行完后,就没有逻辑了。

可以看出,委派生成器具有组织多个子生成器的能力,并将调度者的信息转手传递给子生成器😯。

结尾

在本节中,主要介绍Python中生成器、yield以及yield from的概念与使用,在下一篇中,会接着讨论Python的asyncio框架以及async/await原生协程,最后欢迎学习HackPython的教学课程并感谢您的阅读与支持。

参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值