Python协程双yield导致输入丢失问题及正确实现解析
协程输入丢失问题解析及正确实现原理
问题背景
我有一个名为DICTIONARY的字典,键为英文字母,值是对应字母开头的单词,初始内容如下:
DICTIONARY = { 'a': 'apple', 'b': 'banana', 'c': 'cat', 'd': 'dog', ... }
我需要编写一个名为alphabet的协程,接收字母输入并返回DICTIONARY中对应的单词。
初始协程的问题原因
最初编写的协程使用两个yield语句:
def alphabet(): while True: ch = yield yield DICTIONARY[ch]
测试时出现输入丢失,发送'b'返回None,输出不符合预期:
# 测试代码 coro = alphabet() next(coro) print(coro.send('a')) # 输出 apple print(coro.send('b')) # 输出 None print(coro.send('c')) # 输出 cat
问题出在协程的执行流程上,一步步拆解:
- 调用
next(coro)时,协程执行到ch = yield,暂停在这里,等待外部发送值。 - 第一次
send('a'):把'a'赋值给ch,协程继续执行到yield DICTIONARY[ch],返回'apple',此时协程暂停在这个yield语句。 - 第二次
send('b'):协程从yield DICTIONARY[ch]的位置恢复,进入下一次循环,执行到ch = yield——这里的yield没有附带返回值,所以本次send('b')的返回结果是None,同时把'b'赋值给ch,协程暂停在ch = yield。 - 第三次
send('c'):把'c'赋值给ch,协程执行到yield DICTIONARY[ch],返回'cat',完成本次循环。
简单说,两个yield导致每次send会交替触发“接收输入”和“返回结果”,中间的一次send只能完成接收,无法返回对应的值,所以出现None。
正确协程的实现原理
修改后的协程用单个yield解决了问题:
# 最终正确的协程实现 def alphabet(letter='a'): while True: letter = yield DICTIONARY[letter]
它的执行流程是连贯的,每一次send都会完成“接收输入→返回对应结果”的闭环:
- 初始化协程
coro = alphabet(),调用next(coro):协程执行letter = yield DICTIONARY[letter],先计算右侧的DICTIONARY['a'](因为默认参数是'a'),然后yield返回'apple',协程暂停,此时等待外部发送值来更新letter。 - 第一次
send('b'):把'b'赋值给letter,协程进入下一次循环,执行letter = yield DICTIONARY[letter],计算DICTIONARY['b']得到'banana',yield返回该值,协程暂停。 - 第二次
send('c'):把'c'赋值给letter,循环中执行yield DICTIONARY['c']返回'cat',协程暂停。
这个写法的核心是用单个yield同时完成“返回结果”和“接收下一次输入”的操作:每次yield返回当前letter对应的单词,同时等待外部发送的新值来更新letter,下一次循环就用新的letter返回对应结果,完美匹配“输入一个字母→返回对应单词”的需求。
内容的提问来源于stack exchange,提问作者ERJAN
相关产品推荐
相关产品推荐

