python 数据库操作 yeild from_python中的yield和yield from

学到了一点,就写一点吧:

本文不区分生成器和协程,且把生成器直接当做协程,对于from inspect import iscoroutine中的async协程类型一无所知,

开始:

yield: 流程控制工具: 可以把控制器让步给中心调度程序,从而激活其他协程.(简单说就是实现线程中任务切换)

协程四个状态:

1.'GEN_CREATED': 等待开始执行

2.'GEN_RUNNING': 解释器正执行

3.'GEN_SUSPENDED': 在yield表达式暂停suspended: /sə'spendid/ 暂停的

4.'GEN_CLOSED': 执行结束

协程主要解决python中的高并发问题,是python无法充分利用多核的替代方案.(加粗的部分是我自己说的,有待考证)

以下通过几个案例来浅入浅出的来简单了解yield和yield from.

第一个例子: 简单使用yield

1 deffunc():2 print('start...')3 x = yield

4 print('end, recived value:', x)5 returnx6

7

8 a =func()9 print(getgeneratorstate(a)) #GEN_CREATED 状态1

10 print('***', next(a)) #*** None 用next预激活,此步运行了状态2,进入状态3,yield右侧没有表达式,默认返回None

11 print(getgeneratorstate(a)) #GEN_SUSPENDED 状态3,在x = yield暂停(执行了右边,还未给x赋值)

12 try:13 a.send(666) #给yield发送数据, 执行x = 666,打印: end, recived value: 666 并报错StopIteration

14 #此时协程继续向下执行,知道遇到下一个yield,或终止(StopIteration)

15 except StopIteration as s: #捕获异常,并获取返回值

16 print(s, s.value) #666 666

17 print(getgeneratorstate(a)) #GEN_CLOSED

18

19 #第一个next(a): 预激活协程, 让协程向前运行到第一个yield, 准备好作为活跃的协程使用.

第一个yield

第二个例子: 继续简单使用yield

1 defaverager():2 total = 0.0

3 count =04 average =None5 whileTrue:6 term = yield average #yield分左右,右边使用用局部作用域的值(average=None),左边若客户端有send值过来,则取send的值,没有,则默认为None(term为None)

7 #此时yield会将右侧的average值生产出去,语句为: a = next(generator),此时a = average = None(预激活时)

8 total +=term9 count += 1

10 average = total / count #无限循环进行均值的计算,客户端可以无限次send数据

11

12

13 #上面的代码是未完成的生成器代码(看作DEMO吧), 其实现的效果为: 只要客户端一直向协程发送数据(send(data)), 协程就会一直计算.

14 a =averager()15 b =next(a)16 print(b) #None

17 print(a.send(10)) #10.0

18 print(a.send(20)) #15.0

19 print(a.send(30)) #20.0

第二个yield

第三个例子: 装饰器自动预激活(懒人专属)

1 #第三个例子: 用装饰器预激活协程

2 from functools importwraps3

4

5 defcoroutine_prime(func):6 @wraps(func) #wraps包装: 被包装函数会保留自己的属性

7 def primer(*args, **kwargs):8 gen = func(*args, **kwargs)9 next(gen) #在装饰器中实现预激活功能

10 returngen11

12 returnprimer13

14

15 @coroutine_prime #使用装饰器

16 defaverager():17 total = 0.0

18 count =019 average =None20 whileTrue:21 term = yieldaverage22 total +=term23 count += 1

24 average = total /count25

26

27 a = averager() #此时已经预激活生成器,不用再调用next()函数

28 print(a.send(10)) #10.0

29 print(a.send(20)) #15.0

30 print(a.send(30)) #20.0

装饰器实现预激活

第四个例子: yield转变为yield from的简单机制

1 from inspect importgetgeneratorstate2

3

4 classDemoException(Exception):5 """异常示例"""

6

7

8 @coroutine_prime9 defdemo_func():10 print('start...')11 try:12 whileTrue:13 try:14 x = yield 666

15 exceptDemoException:16 print("It's DemoException.Go on...")17 else:18 print('received:', x)19 finally:20 print("The End!.")21

22

23 a = demo_func() #实例化,预激活 打印: start...

24 a.send(10) #received: 10

25 a.send(20) #received: 20

26 print(a.throw(DemoException)) #It's DemoException.Go on... 666 throw返回值为下一个yield右侧的值

27 #print(a.throw(ZeroDivisionError)) # The End!. 传入未处理异常,直接整个程序向上冒泡至结束

28 print(getgeneratorstate(a)) #GEN_SUSPENDED

29 a.close() #The End!

30 print(getgeneratorstate(a)) #GEN_CLOSED

yield to yield from

第五个例子: 简单使用yield from

其实在上面的例子一里面已经有协程返回值的影子了:

...

try:

a.send(666)  # 给yield发送数据, 执行x = 666,打印: end, recived value: 666并报错StopIteration

# 此时协程继续向下执行,知道遇到下一个yield,或终止(StopIteration)

except StopIteration as s:# 捕获异常,并获取返回值

print(s, s.value) # 666 666

想获取整个协程的返回值,必然需要等到协程终止,而判断协程终止必然会有StopIteration情况出现;

而yield from则集中处理了这些异常,并且实现返回值的功能.(这也是我说例子4是yield转变为yield from的简单机制的原因)

yield from 可以简单理解为: for x in iterable: yield x;即生成器嵌套.

但是yield from 的功能不仅如此.

其主要功能为:

打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,

还可以直接传入异常,而不用在协程中添加大量处理异常的代码.

1 from collections importnamedtuple2

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

5

6 #子生成器: 原始逻辑处理

7 defaverager():8 total = 0.0

9 count =010 average =None11 whileTrue:12 term = yield #位置3

13 if term is None: #位置6

14 break

15 total +=term16 count += 1

17 average = total /count18 returnResult(count, average)19

20

21 #委派生成器: yield from: 中间管道,连接子生成器和客户端,让双方数据直接对接

22 defgrouper(results, key):23 while True: #位置7

24 results[key] = yield from averager() #位置2

25

26

27 defreport(results):28 #格式化打印结果

29 for key, result insorted(results.items()):30 group, unit = key.split(";")31 print('{:2}{:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))32

33

34 #客户端调用,调用方,向子生成器输送数据,运行逻辑

35 defmain(data):36 results ={}37 for key, values in data.items(): #位置8

38 group = grouper(results, key) #位置0

39 next(group) #位置1

40 for value in values: #位置4

41 group.send(value)42 group.send(None) #位置5

43

44 #print(results)

45 report(results)46

47

48 data ={49 'girls;kg':50 [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],51 'girls;m':52 [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],53 'boys;kg':54 [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],55 'boys;m':56 [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],57 }58

59 if __name__ == '__main__':60 main(data)

yield from的简单案例

代码详解:(看的比较累的话可以先自己多运行下代码)

1.main函数处开始调用,到位置1处调用grouper,并执行next()进行预激活;(这里表明: yield from 和 自动预激活装饰器不兼容)

2.进入grouper()函数,进行while True循环,执行到yield from处(位置2),然后到该处暂停,开始执行averager()函数;(委派生成器开始阻塞,只作为调用通道)

3.进入averager()函数,一直运行到位置3处,term = yield,暂停在"="右侧,开始执行yield表达式,此时yield右侧为空,返回None给客户端(main);

4.回到1处(None值接不接收无所谓),进入位置4处的for循环,开始遍历values,并将value的值send到位置3处;(此时子生成器和客户端已经实现数据对接)

5.此时运行位置3处"="的左侧,即term = value,term拿到客户端发送过来的值,然后如上进行循环计算;

6.直到位置4处的循环结束,进入位置5,客户端发送给term一个None的值,子生成器进入位置6,中断循环,并return具名元组Result到位置2;

7.此时运行位置2处"="的左侧,即results[key] = Result(count, average),赋值完毕后,到位置7处,开始下一个循环,又开始调用averager();

8.直到位置8处的for循环完毕,将results字典传入report函数打印出结果,整个流程结束;

9.至此,关于客户端(调用方,main函数)通过委派生成器(yield form, grouper函数)和子生成器(averager函数)完成数据对接完整过程完毕.

上面的简单案例是一个委派生成器(yield from)和一个子生成器连接在一起,而yield from往往和asynicio模块一起做异步编程,虽然我不了解,但是肯定很牛X,学一点点先打个基础,为以后asynicio模块的学习做个准备吧.

以上,均为读"流畅的python"第16章'协程'后自己总结,内容必定不咋滴,待学有所成后再来自行校正.

End!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值