[Python-ideas] Using yield inside a comprehension.

Oscar Benjamin oscar.j.benjamin at gmail.com
Tue Nov 26 15:32:30 CET 2013


On 26 November 2013 13:54, Jonathan Slenders <jonathan at slenders.be> wrote:
>
> Where do I find the PEP that describes that the following statement assigns
> a generator object to `values`?

I don't think there was a PEP for this but it's a consequence of the
change to binding in list comprehensions introduced in Python 3.x
which is mentioned here:
http://python-history.blogspot.co.uk/2010/06/from-list-comprehensions-to-generator.html

Essentially this:

   > values = [ (yield x) for x in range(10) ]

Translates to the following in Python 2.x:

_tmp = []
for x in range(10):
    _tmp.append((yield x))
values = _tmp

However in 3.x it translates to something like:

def _tmpfunc():
    _tmp = []
    for x in range(10):
        _tmp.append((yield x))
    return _tmp
values = _tmpfunc()

This change was made to prevent the comprehension variable from
leaking to the enclosing scope, but as you say if the code is nested
in a function then it affects which function contains the yield
statement and the presence of a yield statement radically alters the
behaviour of a function. So in 2.x the enclosing function must become
a generator function. However in 3.x the function that is supposed to
implement the list comprehension is changed into a generator function
instead.

$ python3
Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:03:43) [MSC v.1600
32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> values = [(yield x) for x in range(10)]
>>> values
<generator object <listcomp> at 0x00E24508>
>>> def _tmpfunc():
...     _tmp = []
...     for x in range(10):
...         _tmp.append((yield x))
...     return _tmp
...
>>> values = _tmpfunc()
>>> values
<generator object _tmpfunc at 0x00E2F7B0>

> I assume it's equivalent to the following:
> values = (x for x in range(10))

It will yield the same values but it will also build a list of Nones
and attach it to StopIteration:

>>> values = [(yield x) for x in range(3)]
>>> values
<generator object <listcomp> at 0x00E1CCD8>
>>> next(values)
0
>>> next(values)
1
>>> next(values)
2
>>> next(values)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: [None, None, None]

> The reason for asking this is that I see no point in using the first syntax
> and that the first has another meaning in Python 2.7, if used inside a
> function.

This has been mentioned before and AFAICT it is an unintended artefact
of the new definitions for comprehensions and yield and the fact that
'return _tmp' works inside a generator function. I haven't seen a use
for this behaviour yet.


Oscar


More information about the Python-ideas mailing list