<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>