Python基础学习之六yield

协程定义

协程的底层架构是在pep342 中定义,并在python2.5 实现的。

python2.5 中,yield关键字可以在表达式中使用,而且生成器API中增加了 .send(value)方法。生成器可以使用.send(...)方法发送数据,发送的数据会成为生成器函数中yield表达式的值。

协程是指一个过程,这个过程与调用方协作,产出有调用方提供的值。因此,生成器可以作为协程使用。

除了 .send(...)方法,pep342 和添加了 .throw(...)(让调用方抛出异常,在生成器中处理)和.close()(终止生成器)方法。

python3.3后,pep380对生成器函数做了两处改动:


生成器可以返回一个值;以前,如果生成器中给return语句提供值,会抛出SyntaxError异常。



引入yield from 语法,使用它可以把复杂的生成器重构成小型的嵌套生成器,省去之前把生成器的工作委托给子生成器所需的大量模板代码。

协程生成器的基本行为

首先说明一下,协程有四个状态,可以使用inspect.getgeneratorstate(...)函数确定:


GEN_CREATED # 等待开始执行



GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)



GEN_SUSPENDED # 在yield表达式处暂停



GEN_CLOSED # 执行结束


#! -*- coding: utf-8 -*-

import inspect

# 协程使用生成器函数定义:定义体中有yield关键字。

defsimple_coroutine():

    print('-> coroutine started')    # yield 在表达式中使用;如果协程只需要从客户那里接收数据,yield关键字右边不需要加表达式(yield默认返回None)

x = yield

    print('-> coroutine received:', x)



my_coro = simple_coroutine()# 和创建生成器的方式一样,调用函数得到生成器对象。

print(inspect.getgeneratorstate(my_coro))

# 协程处于 GEN_CREATED (等待开始状态)my_coro.send(None)

# 首先要调用next()函数,因为生成器还没有启动,没有在yield语句处暂停,所以开始无法发送数据

# 发送 None 可以达到相同的效果 my_coro.send(None) 

next(my_coro)# 此时协程处于 GEN_SUSPENDED (在yield表达式处暂停)

print(inspect.getgeneratorstate(my_coro))

# 调用这个方法后,协程定义体中的yield表达式会计算出42;现在协程会恢复,一直运行到下一个yield表达式,或者终止。

my_coro.send(42)

print(inspect.getgeneratorstate(my_coro))

运行上述代码,输出结果如下

GEN_CREATED

-> coroutine started

GEN_SUSPENDED

-> coroutine received: 42# 这里,控制权流动到协程定义体的尾部,导致生成器像往常一样抛出StopIteration异常

Traceback (most recent call last):

File "/Users/gs/coroutine.py", line 18, in <module>

    my_coro.send(42)

StopIteration

send方法的参数会成为暂停yield表达式的值,所以,仅当协程处于暂停状态是才能调用send方法。

如果协程还未激活(GEN_CREATED 状态)要调用next(my_coro) 激活协程,也可以调用my_coro.send(None)

如果创建协程对象后立即把None之外的值发给它,会出现下述错误:

>>> my_coro = simple_coroutine()

>>> my_coro.send(123)


Traceback (most recent call last):

File "/Users/gs/coroutine.py", line 14, in <module>

    my_coro.send(123)

TypeError: can't send non-None value to a just-started generator

仔细看错误消息

can't send non-None value to a just-started generator

最先调用next(my_coro) 这一步通常称为”预激“(prime)协程---即,让协程向前执行到第一个yield表达式,准备好作为活跃的协程使用。

再看一个两个值得协程

defsimple_coro2(a):

    print('-> coroutine started: a =', a)

b = yield a

    print('-> Received: b =', b)

c = yield a + b

    print('-> Received: c =', c)


my_coro2 = simple_coro2(14)

print(inspect.getgeneratorstate(my_coro2))# 这里inspect.getgeneratorstate(my_coro2) 得到结果为 GEN_CREATED (协程未启动)

next(my_coro2)

# 向前执行到第一个yield 处 打印 “-> coroutine started: a = 14”

# 并且产生值 14 (yield a 执行 等待为b赋值)

print(inspect.getgeneratorstate(my_coro2))

# 这里inspect.getgeneratorstate(my_coro2) 得到结果为 GEN_SUSPENDED (协程处于暂停状态)

my_coro2.send(28)

# 向前执行到第二个yield 处 打印 “-> Received: b = 28”

# 并且产生值 a + b = 42(yield a + b 执行 得到结果42 等待为c赋值)

print(inspect.getgeneratorstate(my_coro2))

# 这里inspect.getgeneratorstate(my_coro2) 得到结果为 GEN_SUSPENDED (协程处于暂停状态)

my_coro2.send(99)

# 把数字99发送给暂停协程,计算yield 表达式,得到99,然后把那个数赋值给c 打印 “-> Received: c = 99”

# 协程终止,抛出StopIteration

运行上述代码,输出结果如下

GEN_CREATED

-> coroutine started: a = 14

GEN_SUSPENDED

-> Received: b = 28

-> Received: c = 99

Traceback (most recent call last):

File "/Users/gs/coroutine.py", line 37, in <module>

    my_coro2.send(99)

StopIteration

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容