scope of generators, class variables, resulting in global na
Arnaud Delobelle
arnodel at googlemail.com
Wed Feb 24 11:14:00 EST 2010
Nomen Nescio wrote:
> Hello,
>
> Can someone help me understand what is wrong with this example?
>
> class T:
> A = range(2)
> B = range(4)
> s = sum(i*j for i in A for j in B)
>
> It produces the exception:
>
> <type 'exceptions.NameError'>: global name 'j' is not defined
It's due to scoping rules for classes and/or how generator expressions
are compiled. When a function definition is executed from within the
body of a class, the body of the class doesn't act as an outer scope
for it. E.g.
class A:
x = 2
def f(self): return x
When f is defined (technically, when the closure is made), the name
'x' is not bound to 2 but is considered to be in the global namespace
(unless the class is defined within a function for example). Now
generator expressions are defined as generator functions so your
example is akin to something like:
class T:
A = range(2)
B = range(4)
def _gen(L):
for i in L:
for j in B:
yield i*j
s = sum(_gen(A))
(From memory, I might be wrong on some details)
Now looking at the above, when _gen is defined, B is considered to be
belonging to the global namespace, therefore if it is not defined
there a NameError will be thrown.
Now a safe workaround to this would be:
class T:
A = range(2)
B = range(4)
s = (lambda A=A, B=B: sum(i*j for i in A for j in B))()
The lambda form is evaluated when the body of the class is executed,
binding the names A and B to the objects you want in the generator
expression.
I remember suggesting a while ago that all generator expressions be
implicitely wrapped like the one above in order to avoid such
surprises. I can't quite remember what the arguments against were,
but you can probably find them in the archives!
--
Arnaud
More information about the Python-list
mailing list