Python函数的正确类型注释

Python函数的正确类型注释,python,generator,coroutine,static-typing,mypy,Python,Generator,Coroutine,Static Typing,Mypy,读了伊莱·本德斯基的文章后,我想 查看他的示例在Python3下运行 并为生成器添加适当的类型注释 我成功地完成了第一部分(但没有使用async defs或yield froms,我基本上只是移植了代码,因此任何改进都是受欢迎的) 但我需要一些关于协同程序类型注释的帮助: #!/usr/bin/env python3 from typing import Callable, Generator def unwrap_protocol(header: int=0x61,

读了伊莱·本德斯基的文章后,我想

  • 查看他的示例在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 
$
但我非常确信,我可以做得比在类型规范中使用
生成器
基类更好(就像我对
可调用
所做的那样)。我知道它需要3个类型参数(
Generator[A,B,C]
),但我不确定它们在这里是如何指定的


欢迎任何帮助。

我自己找到了答案

我搜索了一下,但是没有找到关于
Generator
的3个类型参数的文档,除了真正隐晦地提到

class typing.Generator(Iterator[T_co], Generic[T_co, T_contra, V_co])
幸运的是,(这一切的开始)更有帮助:

“生成器函数的返回类型可以由typing.py模块提供的泛型类型生成器[yield\u type,send\u type,return\u type]进行注释:

基于此,我能够注释我的生成器,并看到
mypy
确认我的分配:

from typing import Callable, Generator

# A protocol decoder:
#
# - yields Nothing
# - expects ints to be `send` in his yield waits
# - and doesn't return anything.
ProtocolDecodingCoroutine = Generator[None, int, None]

# A frame consumer (passed as an argument to a protocol decoder):
#
# - yields Nothing
# - expects List[int] to be `send` in his waiting yields
# - and doesn't return anything.
FrameConsumerCoroutine = Generator[None, List[int], None]


def unwrap_protocol(header: int=0x61,
                    footer: int=0x62,
                    dle :int=0xAB,
                    after_dle_func: Callable[[int], int]=lambda x: x,
                    target: FrameConsumerCoroutine=None) -> ProtocolDecodingCoroutine:
    ...

def frame_receiver() -> FrameConsumerCoroutine:
    ...
我通过交换类型的顺序来测试我的作业,然后如预期的那样,
mypy
抱怨并要求正确的类型(如上所示)

完整的代码


我将把这个问题留待几天,以防有人想插话——特别是在使用Python 3.5的新协同程序样式方面(
async def
,等等)-如果您有一个使用
yield
的简单函数,那么您可以使用
迭代器
类型来注释其结果,而不是
生成器

from typing import Iterator

def count_up() -> Iterator[int]:
    for x in range(10):
        yield x
在撰写本文时,还明确提到了如何处理异步情况(非异步示例已经在接受的答案中提到)

从那里引用:

async def echo_round()->AsyncGenerator[int,float]:
发送=收益率0
发送>=0.0时:
四舍五入=等待四舍五入(已发送)
发送=收益率四舍五入
(第一个参数是屈服类型,第二个参数是发送类型)或对于简单情况(发送类型为无)

async def infinite_流(开始:int)->AsyncIterator[int]:
尽管如此:
产量起点
开始=等待增量(开始)

关于
async def
和friends——它们目前不受mypy支持,但正在积极开发中/应该在不久的将来准备就绪!有关更多详细信息,请参阅和。仅供参考,mypy 0.4.4(具有对async/await的实验性支持)。您可以在中找到有关键入协同路由和async/await的更多信息。目前,我不认为键入模块的文档本身提到了与async/await相关的内容,但这可能会在未来几天内得到解决。谢谢,Michael-将对此进行检查。Python 3.8.3文档更具描述性:生成器可以由泛型类型生成器[YieldType,SendType,ReturnType]进行注释。“这是指生成器只生成值,既不接收也不返回值。
from typing import Iterator

def count_up() -> Iterator[int]:
    for x in range(10):
        yield x