Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!ecngs!feeder2.ecngs.de!newsfeed.freenet.ag!news2.euro.net!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'compiler': 0.07; 'variable,': 0.07; 'python': 0.08; 'block.': 0.09; 'context.': 0.09; 'foo': 0.09; 'foo,': 0.09; 'received:mail- lpp01m010-f46.google.com': 0.09; 'run.': 0.09; 'url:peps': 0.09; 'variables.': 0.09; 'def': 0.13; 'stored': 0.13; '"x"': 0.16; '"y"': 0.16; 'accesses': 0.16; 'attribute,': 0.16; 'bieber': 0.16; 'cell.': 0.16; 'cliff': 0.16; 'declaimed': 0.16; 'definition,': 0.16; 'foo(object):': 0.16; 'iterator': 0.16; 'iterator,': 0.16; 'scope,': 0.16; 'scope.': 0.16; 'two.': 0.16; 'why,': 0.16; 'cc:addr:python-list': 0.16; 'wrote:': 0.18; '>>>': 0.18; 'reason,': 0.18; 'appears': 0.19; 'seems': 0.20; 'cheers,': 0.20; 'cc:no real name:2**0': 0.21; 'trying': 0.21; '(but': 0.21; "doesn't": 0.22; 'header:In-Reply-To:1': 0.22; '(or': 0.22; '(b)': 0.23; 'found,': 0.23; 'loop,': 0.23; 'url:dev': 0.23; 'defined': 0.24; 'starts': 0.24; 'explains': 0.24; 'stack': 0.24; 'subject:List': 0.25; 'cc:2**0': 0.26; 'code': 0.26; 'function': 0.27; 'tried': 0.27; 'fact': 0.27; 'variable': 0.28; 'second': 0.28; 'message-id:@mail.gmail.com': 0.29; 'work:': 0.29; 'class': 0.29; 'example': 0.29; 'problem': 0.29; 'cc:addr:python.org': 0.29; 'pm,': 0.29; 'outer': 0.30; 'subject:skip:i 10': 0.30; 'actually': 0.31; 'anonymous': 0.32; 'received:209.85.215.46': 0.32; 'does': 0.32; 'tue,': 0.32; 'list': 0.32; 'there': 0.33; "can't": 0.33; 'it.': 0.33; 'lee': 0.34; 'loop': 0.34; 'subject:/': 0.34; '(a)': 0.34; 'frame': 0.34; 'however,': 0.35; 'url:python': 0.35; 'issue': 0.37; '(to': 0.37; 'instead,': 0.37; 'variables': 0.37; 'created': 0.37; 'but': 0.37; 'reference': 0.37; 'received:google.com': 0.37; 'happens': 0.38; 'received:209.85': 0.38; 'think': 0.38; 'url:org': 0.39; 'received:209.85.215': 0.39; 'received:209': 0.39; 'hope': 0.61; 'is.': 0.63; 'ever': 0.64; 'strange': 0.68; 'dennis': 0.73 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc:content-type:content-transfer-encoding; bh=Jd4CDtymAj2Ov7j+C6ecGVxiVBjWy7bVx02Xo8AmapU=; b=WOwqyjs21y9HVp0jlPcsQws+wG9gfibaPacKeR//t7wLNm8+MPomqsqeAX1lCFIKne X5029ymIVb7/knKcbHPyLiHoIEJILZzlw+oBNP/FdmdwfEpW0eNCQnLBbJ4yR7LSkPX0 ZZURPbuQLWFWUnDrrWDgP+jaBS7AS43aeYGb403CZv9aLcaZASVg1Kk5XWJXz7aEyy9I hh343NfUcJB4rv1kM2dWwAimhTTcHhlZSM5lia4+xpt1QK8Du3F45t1EJd+/EpvK8ZWI icmFNpwZcIIeC0q3MHVfxUnPOxcHg06YwypZDlV4rp8MVRgdhhfYELkO0kAAg4ZlglLv CsIQ== MIME-Version: 1.0 In-Reply-To: References: <1332275002.10958.7.camel@jcdyer-laptop> From: Ian Kelly Date: Tue, 20 Mar 2012 16:50:03 -0600 Subject: Re: List comprehension/genexp inconsistency. To: Dennis Lee Bieber Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Cc: python-list@python.org X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 78 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1332283836 news.xs4all.nl 6939 [2001:888:2000:d::a6]:59054 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:21959 On Tue, Mar 20, 2012 at 3:16 PM, Dennis Lee Bieber wrote: > On Tue, 20 Mar 2012 16:23:22 -0400, "J. Cliff Dyer" > declaimed the following in > gmane.comp.python.general: > >> >> When trying to create a class with a dual-loop generator expression in a >> class definition, there is a strange scoping issue where the inner >> variable is not found, (but the outer loop variable is found), while a >> list comprehension has no problem finding both variables. >> > =A0 =A0 =A0 =A0Read http://www.python.org/dev/peps/pep-0289/ -- in partic= ular, look > for the word "leak" No, this has nothing to do with the loop variable leaking. It appears to have to do with the fact that the variables and the generator expression are inside a class block. I think that it's related to the reason that this doesn't work: class Foo(object): x =3D 42 def foo(): print(x) foo() In this case, x is not a local variable of foo, nor is it a global. In order for foo to access x, it would have to be a closure -- but Python can't make it a closure in this case, because the variable it accesses is (or rather, will become) a class attribute, not a local variable of a function that can be stored in a cell. Instead, the compiler just makes it a global reference in the hope that such a global will actually be defined when the code is run. For that reason, what surprises me about Cliff's example is that a generator expression works at all in that context. It seems to work as long as it contains only one loop, but not if it contains two. To find out why, I tried disassembling one: >>> class Foo(object): ... x =3D 42 ... y =3D 12 ... g =3D (a+b for a in range(x) for b in range(y)) ... >>> dis.dis(Foo.g.gi_code) 4 0 LOAD_FAST 0 (.0) >> 3 FOR_ITER 34 (to 40) 6 STORE_FAST 1 (a) 9 LOAD_GLOBAL 0 (range) 12 LOAD_GLOBAL 1 (y) 15 CALL_FUNCTION 1 18 GET_ITER >> 19 FOR_ITER 15 (to 37) 22 STORE_FAST 2 (b) 25 LOAD_FAST 1 (a) 28 LOAD_FAST 2 (b) 31 BINARY_ADD 32 YIELD_VALUE 33 POP_TOP 34 JUMP_ABSOLUTE 19 >> 37 JUMP_ABSOLUTE 3 >> 40 LOAD_CONST 0 (None) 43 RETURN_VALUE So that explains it. Notice that "x" is never actually accessed in that disassembly; only "y" is. It turns out that the first iterator [range(x)] is actually created before the generator ever starts executing, and is stored as an anonymous local variable on the generator's stack frame -- so it's created in the class scope, not in the generator scope. The second iterator, however, is recreated on every iteration of the first iterator, so it can't be pre-built in that manner. It does get created in the generator scope, and when that happens it blows up because it can't find the variable, just like the function example above. Cheers, Ian