[Python-3000] Type annotations: annotating generators (original) (raw)

Guido van Rossum guido at python.org
Fri May 19 02:40:20 CEST 2006


On 5/18/06, Collin Winter <collinw at gmail.com> wrote:

In working on notes for the type annotations PEP, I've hit some ambiguous/unresolved issues in the BDFL annotation syntax (drawing primarily from [1] and other blog posts):

As a quick recap, the syntax looks something like this: """def foo(a: int, b: float, c: int = 5) -> list[int]""" In looking through all of Guido's blog posts on the subject -- and all the comments on them -- I haven't seen anyone consider the case of generators. Assuming that "->" makes assertions only about the function's return type, if I were to write a generator with the following type, """def myrange(min: Number, max: Number) -> Number""" it would blow up because the function returns a generator, not a Number.

My first response: specify the return type as Generator[Number] so the whole thing would look like

def my_range(min: Number, max: Number) -> Generator[Number]: ...

Of course, "->" could be DWIMmy, in the sense that it knows whether it's being used in a function or generator context. This doesn't solve the other problem with annotating generators, though: send().

With the BDFL syntax, there's no way to add annotations for a generator's send() method. One might suggest something like """def myrange(min: Number, max: Number) <- Number -> Number""" where "<- Number" indicates that the generator's send() only accepts Numbers, but this feels terribly kludgy to me.

I don't like adding syntax specific to generators (especially since most of the time send() won't be used at all).

I could extend my Generator[Number] example by also allowing Generator[Number, Number] where the 2nd type would specify the argument accepted by send(). (Making it 2nd makes it easy to omit.)

Or if you don't like this (I'm not crazy about letting people guess what the second type is for either) you could write Generator(Number, send=Number) or even Generator(returns=Number, send=Number). I think it's fine to specify the return type of the generator by position and the rest by keyword since the return type is always present. Generator[Number] and Generator(Number) could mean the same thing assuming Generator is not a real type like list but a pseudo type only used for type annotations, like Sequence, Mapping, Iterator etc.

My own proposal is to use keywords in the vein of "returns", "yields" and "issent" (don't quibble with the names; the actual names can be decided later). Under this modified syntax, the above send() example looks something like this:

"""def myrange(min: Number, max: Number) issent Number, yields Number"""

That's very ugly IMO.

In the same spirit, "->" would be replaced with "returns":

"""def foo(a: int, b: float, c: int = 5) returns list[int]"""

I don't like that: 'returns' would have to be a reserved word (there are no context-dependent reserved words in Python, 'import as' notwithstanding). It seems you're making everyone suffer for a problem that occurs only for generators.

To combat overly long lines, users could wrap the annotation clauses to the next line:

""" def myrange(min: Number, max: Number) issent Number, yields Number """

The lexer doesn't know enough about the grammer to know that the newline after the comma should be ignored; adding this to the grammar would be tricky too since presumably the newline would remain optional.

-- --Guido van Rossum (home page: http://www.python.org/~guido/)



More information about the Python-3000 mailing list