<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div class="gmail_extra">(Note: this is an off-topic side thread, unrelated to assignment expressions. Inline comment below.)</div><br></div><div class="gmail_quote">On Fri, May 11, 2018 at 9:08 AM, Chris Angelico <span dir="ltr"><<a href="mailto:rosuav@gmail.com" target="_blank">rosuav@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="gmail-">On Fri, May 11, 2018 at 9:15 PM, Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> wrote:<br>
> * *maybe* discover that even the above expansion isn't quite accurate, and<br>
> that the underlying semantic equivalent is actually this (one way to<br>
> discover this by accident is to have a name error in the outermost iterable<br>
> expression):<br>
><br>
>     def _genexp(_outermost_iter):<br>
>         for x in _outermost_iter:<br>
>             yield x<br>
><br>
>     _result = _genexp(_outermost_iter)<br>
><br>
> * and then realise that the optimised list comprehension form is essentially<br>
> this:<br>
><br>
>     def _listcomp(_outermost_iter):<br>
>         result = []<br>
>         for x in _outermost_iter:<br>
>             result.append(x)<br>
>         return result<br>
><br>
>     _result = _listcomp(data)<br>
><br>
> Now that "yield" in comprehensions has been prohibited, you've learned all<br>
> the edge cases at that point<br>
<br>
</span>Not quite! You missed one, just because comprehensions aren't weird<br>
enough yet. AFAIK you can't tell with the list comp, but with the<br>
genexp you can (by not iterating over it).<br>
<span class="gmail-"><br>
>     def _genexp(_outermost_iter):<br>
>         for x in _outermost_iter:<br>
>             yield x<br>
><br>
</span>>     _result = _genexp(data)<br>
<br>
It's actually this:<br>
<span class="gmail-"><br>
     def _genexp(_outermost_iter):<br>
         for x in _outermost_iter:<br>
             yield x<br>
<br>
</span>     _result = _genexp(iter(_outermost_iter))<br>
<br>
I don't think there's anything in the main documentation that actually<br>
says this, although PEP 289 mentions it in the detaily bits. [1]<br>
<br>
ChrisA<br>
<br>
[1] <a href="https://www.python.org/dev/peps/pep-0289/#the-details" rel="noreferrer" target="_blank">https://www.python.org/dev/<wbr>peps/pep-0289/#the-details</a><br></blockquote></div></div><div class="gmail_extra"><br></div><div class="gmail_extra">I'm not sure this is the whole story. I tried to figure out how often __iter__ is called in a genexpr. I found that indeed I see iter() is called as soon as the generator is brought to life, but it is *not* called a second time the first time you call next(). However the translation you show has a 'for' loop which is supposed to call iter() again. So how is this done? It seems the generated bytecode isn't equivalent to a for-loop, it's equivalent to s while loop that just calls next().</div><div class="gmail_extra"><br></div><div class="gmail_extra">Disassembly of a regular generator:</div><div class="gmail_extra"><br></div><div class="gmail_extra"><span style="font-family:monospace,monospace">def foo(a):<br>  for x in a: yield x<br></span></div><div class="gmail_extra"><span style="font-family:monospace,monospace"><br></span></div><div class="gmail_extra"><span style="font-family:monospace,monospace"><b>  2           0 SETUP_LOOP              18 (to 20)</b><br>              2 LOAD_FAST                0 (a)<br><b>              4 GET_ITER</b><br>        >>    6 FOR_ITER                10 (to 18)<br>              8 STORE_FAST               1 (x)<br>             10 LOAD_FAST                1 (x)<br>             12 YIELD_VALUE<br>             14 POP_TOP<br>             16 JUMP_ABSOLUTE            6<br>        >>   18 POP_BLOCK<br>        >>   20 LOAD_CONST               0 (None)<br>             22 RETURN_VALUE</span><br><span style="font-family:monospace,monospace"><br></span><br clear="all"></div><div class="gmail_extra">But for a generator:</div><div class="gmail_extra"><br></div><div class="gmail_extra"><span style="font-family:monospace,monospace">g = (x for x in C())<br></span></div><div class="gmail_extra"><span style="font-family:monospace,monospace"><br></span></div><div class="gmail_extra"><span style="font-family:monospace,monospace">  1           0 LOAD_FAST                0 (.0)<br>        >>    2 FOR_ITER                10 (to 14)<br>              4 STORE_FAST               1 (x)<br>              6 LOAD_FAST                1 (x)<br>              8 YIELD_VALUE<br>             10 POP_TOP<br>             12 JUMP_ABSOLUTE            2<br>        >>   14 LOAD_CONST               0 (None)<br>             16 RETURN_VALUE</span><br></div><div class="gmail_extra"><br></div><div class="gmail_extra">Note the lack of <span style="font-family:monospace,monospace">SETUP_LOOP</span> and <span style="font-family:monospace,monospace">GET_ITER</span> (but otherwise they are identical).<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">-- <br><div class="gmail_signature">--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div>
</div></div>