2

Writing the following code

def a(n: int):
    for i in range(n):
        yield i

b = a(3)

then asking PyCharm to add type hint to variable b turns the variable declaration into

b: Generator[int, Any, None] = a(3)

What do the Any and None represent? Why does Generator take those type parameters?

user2357112
  • 260,549
  • 28
  • 431
  • 505
Ivan
  • 63,011
  • 101
  • 250
  • 382

1 Answers1

2

The second and third type parameters represent the type that the generator's send takes, and the type that the generator returns.

send is a feature introduced way back in Python 2.5 as part of PEP 342, which extended generators to work as coroutines. In PEP 342, yield becomes an expression, and send is like next, but specifying a value for the yield expression the generator is suspended at. (If the generator is suspended at the beginning rather than at a yield, a non-None value cannot be sent into it.) Looking at the example in the typing.Generator docs:

def echo_round() -> Generator[int, float, str]:
    sent = yield 0
    while sent >= 0:
        sent = yield round(sent)
    return 'Done'

this generator takes floats in send, and returns the rounded value of the send argument.

Generator return values were introduced in Python 3.3 as part of PEP 380, as part of subgenerator delegation support. Before PEP 380, it was very awkward to divide a generator into multiple functions, in part because subgenerators had no mechanism like return to communicate results back to their caller. With PEP 380, a generator can return a value, which will be used as the value of a yield from expression that yields from the generator. In the typing.Generator documentation example, echo_round returns a string.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • The question is why. What the 2 other parameters are supposed to mean? In what cases can they be other than Any and None? – Ivan Nov 05 '18 at 19:53
  • 2
    @Ivan: Read [the docs](https://docs.python.org/3/library/typing.html#typing.Generator); they spell it out there. The second parameter is the type you send to it, the third is the type it returns. Neither type is commonly used by simple generators. The docs do recommend annotating both as `None` for "yield only" generators, but PyCharm doesn't have a way to determine for sure if sending isn't supported I guess. – ShadowRanger Nov 05 '18 at 19:54
  • I've read the docs but can't understand what does it even mean to send something to a generator? ReturnType doesn't make much sense to me either, I've copy-pasted the example to Python to try experimenting but it raises a TypeError: 'ABCMeta' object is not subscriptable. – Ivan Nov 05 '18 at 20:03
  • What are generators other than "yield only"? What else but yield can they do? – Ivan Nov 05 '18 at 20:04
  • @Ivan: That was not at all clear from your question. https://www.python.org/dev/peps/pep-0342/ https://www.python.org/dev/peps/pep-0380/ – user2357112 Nov 05 '18 at 20:07
  • Also see the [`typing.Generator` example in the docs](https://docs.python.org/3/library/typing.html#typing.Generator). – user2357112 Nov 05 '18 at 20:09
  • @Ivan yes, generators have a `.send` method, because they can be used as co-routines see [this question](https://stackoverflow.com/questions/19302530/python-generator-send-function-purpose) for more details – juanpa.arrivillaga Nov 05 '18 at 20:11
  • 1
    @Ivan: I've edited the question and answer to reflect what you say you were actually looking for. – user2357112 Nov 05 '18 at 20:17
  • 1
    @Ivan: `async` and `await` are a different thing that was added later. It's closely related, and it reuses much of the same implementation, but it's not the same. Annotating an `async` coroutine with `Generator` would be incorrect. – user2357112 Nov 05 '18 at 20:19
  • Thank you very much. Any idea why copy-pasting the example to Python raises "ABCMeta object is not subscriptable" TypeError? – Ivan Nov 05 '18 at 20:22
  • 1
    @Ivan: You appear to have used `collections.abc.Generator` instead of `typing.Generator`. – user2357112 Nov 05 '18 at 20:22
  • Thanks again. I thought Generator is to be imported from collections. What's the difference? – Ivan Nov 05 '18 at 20:24
  • @Ivan: `collections.abc.Generator` is a base class that can be inherited from or just used for runtime explicit type checking. `typing.Generator` is for static type annotations (for checks with linters, `mypy` and the like). `typing` types are the only ones that support bracketed syntax on the class itself for use in annotations (using "lookup" to mean "nested annotation"), but that feature doesn't apply to functional classes. – ShadowRanger Nov 08 '18 at 20:11