[Python-Dev] Tricky way of of creating a generator via a comprehension expression
Hrvoje Niksic
hrvoje.niksic at avl.com
Fri Nov 24 04:58:19 EST 2017
Guido van Rossum writes:
> And my mind boggles when considering a generator expression
> containing yield that is returned from a function. I tried this
> and cannot say I expected the outcome:
>
> def f():
> return ((yield i) for i in range(3))
> print(list(f()))
>
> In both Python 2 and Python 3 this prints
>
> [0, None, 1, None, 2, None]
>
> Even if there's a totally logical explanation for that, I still
> don't like it, and I think yield in a comprehension should be
> banned. From this it follows that we should also simply ban
> yield from comprehensions.
>
Serhiy Storchaka writes:
> This behavior doesn't look correct to me and Ivan.
The behavior is surprising, but it seems quite consistent with how
generator expressions are defined in the language. A generator
expression is defined by the language reference as "compact generator
notation in parentheses", which yields (sic!) a "new generator object".
I take that to mean that a generator expression is equivalent to
defining and calling a generator function. f() can be transformed to:
def f():
def _gen():
for i in range(3):
ret = yield i
yield ret
return _gen()
The transformed version shows that there are *two* yields per iteration
(one explicitly written and one inserted by the transformation), which
is the reason why 6 values are produced. The None values come from list
constructor calling __next__() on the generator, which (as per
documentation) sends None into the generator. This None value is yielded
after the "i" is yielded, which is why Nones follow the numbers.
Hrvoje
More information about the Python-Dev
mailing list