在阅读了 Eli Bendersky 关于通过 Python 协程实现状态机 的文章后,我想……
- 查看他的示例在 Python3 下运行
- 并为生成器添加适当的类型注释
我成功地完成了第一部分( _但没有使用 async def
s 或 yield from
s,我基本上只是移植了代码 - 所以任何改进都是最受欢迎的_)。
但是我需要一些关于协程类型注释的帮助:
#!/usr/bin/env python3
from typing import Callable, Generator
def unwrap_protocol(header: int=0x61,
footer: int=0x62,
dle: int=0xAB,
after_dle_func: Callable[[int], int]=lambda x: x,
target: Generator=None) -> Generator:
""" Simplified protocol unwrapping co-routine."""
#
# Outer loop looking for a frame header
#
while True:
byte = (yield)
frame = [] # type: List[int]
if byte == header:
#
# Capture the full frame
#
while True:
byte = (yield)
if byte == footer:
target.send(frame)
break
elif byte == dle:
byte = (yield)
frame.append(after_dle_func(byte))
else:
frame.append(byte)
def frame_receiver() -> Generator:
""" A simple co-routine "sink" for receiving full frames."""
while True:
frame = (yield)
print('Got frame:', ''.join('%02x' % x for x in frame))
bytestream = bytes(
bytearray((0x70, 0x24,
0x61, 0x99, 0xAF, 0xD1, 0x62,
0x56, 0x62,
0x61, 0xAB, 0xAB, 0x14, 0x62,
0x7)))
frame_consumer = frame_receiver()
next(frame_consumer) # Get to the yield
unwrapper = unwrap_protocol(target=frame_consumer)
next(unwrapper) # Get to the yield
for byte in bytestream:
unwrapper.send(byte)
这运行正常…
$ ./decoder.py
Got frame: 99afd1
Got frame: ab14
…还有类型检查:
$ mypy --disallow-untyped-defs decoder.py
$
但我很确定我可以做得更好,而不仅仅是在类型规范中使用 Generator
基类(就像我对 Callable
所做的那样)。我知道它需要 3 个类型参数 ( Generator[A,B,C]
),但我不确定它们在这里是如何指定的。
欢迎任何帮助。
原文由 ttsiodras 发布,翻译遵循 CC BY-SA 4.0 许可协议
我自己想出了答案。
我进行了搜索,但在 Python 3.5.2 的官方打字文档中 没有找到
Generator
的 3 个类型参数的文档 - 除了真正神秘的提及……幸运 的是,最初的 PEP484 (开始了这一切)更有帮助:
_“生成器函数的返回类型可以通过typing.py模块提供的泛型Generator[yield_type, send_type, returntype]注解:
基于此,我能够注释我的生成器,并看到
mypy
确认我的分配:我通过例如交换类型的顺序来测试我的分配 - 然后如预期的那样,
mypy
抱怨并要求正确的(如上所示)。完整代码 可从此处访问。
我会把这个问题悬而未决几天,以防万一有人想插话——尤其是在使用 Python 3.5 的新协程样式方面(
async def
等)——我会很感激你的提示它们将如何在这里使用。