Python 生成器表达式的 PEP 484 类型注释

Python's PEP 484 type annotation for Generator Expression

returns 和 generator expression 函数的正确类型注释是什么?

例如:

def foo():
    return (x*x for x in range(10))

我不知道这是 -> Iterator[int]-> Iterable[int]-> Generator[int, None, None] 还是其他。

如果应该有一种——最好只有一种——显而易见的方法,那么显而易见的方法是什么在吗?

你提到的所有三种形式都列为valid alternatives in documentation,生成器表达式只是创建一个只产生的生成器。

引用 1:

A generator can be annotated by the generic type Generator[YieldType, SendType, ReturnType].

引用 2:

If your generator will only yield values, set the SendType and ReturnType to None

引用 3:

Alternatively, annotate your generator as having a return type of either Iterable[YieldType] or Iterator[YieldType]:

快速说明:您的函数是“return是生成器的常规函数​​”,而不是“生成器函数”。要了解区别,请阅读

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

说明

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

首先,让自己熟悉 python 文档中的 this page,其中定义了最重要的 Python 类型的层次结构。

你可以看到这些表达式 return True:

import typing as t
issubclass(t.Iterator, t.Iterable)
issubclass(t.Generator, t.Iterator)

您还应该在同一页面上注意到 GeneratorIterator 没有的方法。这些方法是 sendthrowclosedocumentation), and they allow you to do more with generators than just simple single passthrough iteration. Check this question for examples of the possibilities with generators: python generator "send" function purpose?

回到选择界面。如果你想让别人像生成器一样使用你的生成器函数的结果,即

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

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

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

def gen(limit: int): -> Iterator[int]
    return iter(list(range(limit)))

那些使用像 Generator 这样的 returned 对象的人(因为他们偷看了实现)会破坏他们的代码。但是,您不应该为此感到困扰,因为他们使用它的方式与合同中指定的方式不同。因此,这种损坏不是您的责任。

简单地说,如果您最终得到 Generator[Something, None, None](两个 None),那么请考虑 Iterable[Something]Iterator[Something]

IteratorIterable 的情况相同。如果您希望您的用户只能通过 iter 函数使用您的对象(因此可以在迭代上下文中使用,例如 [x for x in g]),那么请使用 Iterable。如果您希望他们在对象上同时使用 nextiter,请使用 Iterator.

备注

这种思路主要适用于 returned 值的注释类型。对于参数,您应该根据要在函数内的该对象上使用的接口(阅读:methods/functions)来指定类型。