用于生成器表达式的Python PPE 484类型注释

Che*_*evy 5 python yield generator type-hinting

返回生成器表达式的函数的正确类型注释是什么?

例如:

def foo():
    return (x*x for x in range(10))
Run Code Online (Sandbox Code Playgroud)

我想不通,如果这是-> Iterator[int]-> Iterable[int]-> Generator[int, None, None],或别的东西。

如果应该有一种-最好只有一种-显而易见的方法,那么这里有什么明显的方法呢?

Wlo*_*oHu 11

快速注意:您的函数是“返回生成器的常规函数​​”,而不是“生成器函数”。要了解区别,请阅读此答案

对于你来说foo,我建议使用-> Iterator[int].

解释

这归结为你想要什么样的界面。

首先,让自己熟悉Python 文档中的这个页面,其中定义了最重要的 Python 类型的层次结构。

您可以看到这些表达式返回True

import typing as t
issubclass(t.Iterator, t.Iterable)
issubclass(t.Generator, t.Iterator)
Run Code Online (Sandbox Code Playgroud)

您还应该注意到同一页面上有没有的Generator方法。Iterator这些方法是sendthrowclose文档),它们允许您使用生成器做更多事情,而不仅仅是简单的单次直通迭代。检查此问题以获取生成器可能性的示例:Python 生成器上“发送”函数的目的是什么?

回到选择接口。如果你希望其他人像生成器一样使用你的生成器函数的结果,即

def gen(limit: int): -> Generator[int, None, None]
    for x in range(limit):
        yield x

g = gen(3)
next(g)  # => 0
g.send(10)  # => 1
Run Code Online (Sandbox Code Playgroud)

那么你应该指定-> Generator[int, None, None].

但请注意,以上都是废话。事实上,您可以调用send,但它不会改变执行,因为gen不会对发送的值执行任何操作(没有类似的东西x = yield)。知道了这一点,您就可以限制人们使用的知识gen并将其定义为-> Iterator[int]。通过这种方式,您可以与用户签订合同“我的函数返回整数迭代器,您应该这样使用它”。如果您稍后将实现更改为,例如

def gen(limit: int): -> Iterator[int]
    return iter(list(range(limit)))
Run Code Online (Sandbox Code Playgroud)

那些使用返回对象的人Generator(因为他们查看了实现)的代码将会被破坏。但是,您不应该为此烦恼,因为他们使用它的方式与您合同中指定的方式不同。因此,这种破损不是您的责任。

简而言之,如果您最终得到Generator[Something, None, None](two Nones),则考虑Iterable[Something]Iterator[Something]

Iteratorvs也是如此Iterable。如果您希望用户只能将您的对象与iter函数一起使用(从而在迭代上下文中使用,例如[x for x in g]),则使用Iterable. 如果您希望他们在对象上同时使用next和,请使用。iterIterator

笔记

这种思路主要适用于返回值的带注释类型。对于参数,您应该根据您想要在函数内的该对象上使用的接口(读取:方法/函数)来指定类型。


Łuk*_*ski 5

有问题的人提到的所有三种形式在文档中均被列为有效的替代方案,Generator表达式仅创建仅屈服的Generator。

引用1:

生成器可以用通用类型注释Generator[YieldType, SendType, ReturnType]

引用2:

如果生成器仅产生值,则将SendType和 设置ReturnTypeNone

引用3:

或者,将生成器注释为具有Iterable[YieldType]或的返回类型Iterator[YieldType]