[Python-Dev] Seeming unintended difference between list comprehensions and generator expressions... (original) (raw)

Nick Coghlan ncoghlan at gmail.com
Sat Feb 21 00:14:38 CET 2009


Josiah Carlson wrote:

The behavior of 3.0 WRT list comprehensions behaving the same way as generator expressions is expected, but why generator expressions (generally) don't keep a reference to the class scope during execution seems to be unintended.

It's intended. While arguably not ideal, it would require some pretty major changes to the lexical scoping rules to make them behave any differently.

The translation of (i*i for i in x) is conceptually along the lines of:

def _ge(arg): for i in arg: yield i*i

= _ge(x)

Similarly, a 3.x list comprehension [i*i for i in x] is very roughly translated as:

def _lc(arg): result = [] for i in arg: result.append(i*i) return result

= _lc(x)

Like any function scope inside a class namespace, the body of a genexp (and, in 3.x, comprehension) doesn't have direct access to the class namespace because classes don't play any part in the lexical scoping rules.

Basically, if a generator or 3.x comprehension needs access to a value from a containing class scope anywhere other than the outermost iterator, then it needs to be put into a temporary function and given the extra value as an argument:

.>> class C: ... x = {} ... def _init_constants(d, itr): ... d.update((i, d.get(i, None)) for i in itr) ... _init_constants(x, range(10)) ... del _init_constants ... .>> C.x {0: None, 1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}

(in this toy case, of course, it would be simpler to make the temporary function create and return the constants dictionary, but the above approach with multiple arguments being passed in applies more generally when you need to access multiple existing values from the class scope)

Cheers, Nick.

-- Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia



More information about the Python-Dev mailing list