Nested scopes and class variables
Alex Martelli
aleaxit at yahoo.com
Mon Jan 31 07:16:38 EST 2005
Dave Benjamin <ramen at lackingtalent.com> wrote:
> I ran into an odd little edge case while experimenting with functions that
> create classes on the fly (don't ask me why):
"Why not?". But classes have little to do with it, in my view.
> >>> def f(x):
> ... class C(object):
> ... x = x
You bind x, so x is local (to the class), not free. Videat, classless:
>>> def f(x):
... def g():
... x=x
... return x
... return g
...
>>> z=f(23)
>>> z()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in g
UnboundLocalError: local variable 'x' referenced before assignment
In this example the error is discovered when the body of g executes; in
your case, the body of C executes as part of the class statement, i.e.
when f is called, so the error is discovered earlier.
> "x" clearly is defined, but apparently Python is not looking at the nested
> variable scope to find it. What's stranger is that if I rename the parameter
> "x" to "y", the error goes away:
Why is this strange? There's no name conflict then.
> So, it's not like nested scopes aren't supported in the class block. Rather,
> when it sees "x = x", it seems like Python is determining at that point that
> "x" is a class variable, and refuses to search any further.
That's like saying that nested scopes aren't supported in a function...
when Python sees "x = x", etc etc.
> At the top-level, it works as expected:
>
> >>> x = 5
> >>> class C(object):
> ... x = x
> ...
> >>> C.x
> 5
>
> Any implementation gurus have some insight into what's going on here?
Class bodies and function bodies are compiled slightly differently,
leading to a "handy specialcasing" of globals in the latter example
which is probably what's confusing you. OK, let's try digging into more
detail:
>>> def f(x):
... class C:
... x = x
... return C
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 ('C')
3 BUILD_TUPLE 0
6 LOAD_CONST 2 (<code object C at 0x389860,
file "<stdin>", line 2>)
9 MAKE_FUNCTION 0
12 CALL_FUNCTION 0
15 BUILD_CLASS
16 STORE_FAST 1 (C)
4 19 LOAD_FAST 1 (C)
22 RETURN_VALUE
this shows you where the codeobject for C's body is kept -- constant
number two among f's constants. OK then:
>>> dis.dis(f.func_code.co_consts[2])
2 0 LOAD_GLOBAL 0 (__name__)
3 STORE_NAME 1 (__module__)
3 6 LOAD_NAME 2 (x)
9 STORE_NAME 2 (x)
12 LOAD_LOCALS
13 RETURN_VALUE
Compare with:
>>> def f(x):
... def g():
... x = x
... return x
... return g
...
>>> dis.dis(f)
2 0 LOAD_CONST 1 (<code object g at 0x389620,
file "<stdin>", line 2>)
3 MAKE_FUNCTION 0
6 STORE_FAST 1 (g)
5 9 LOAD_FAST 1 (g)
12 RETURN_VALUE
>>>
and:
>>> dis.dis(f.func_code.co_consts[1])
3 0 LOAD_FAST 0 (x)
3 STORE_FAST 0 (x)
4 6 LOAD_FAST 0 (x)
9 RETURN_VALUE
See the difference? In a function, the 'x = x' compiles into LOAD_FAST,
STORE_FAST, which only looks at locals and nowhere else. In a
classbody, it compiles to LOAD_NAME, STORE_NAME, which looks at locals
AND globals -- but still not at closure cells...
Alex
More information about the Python-list
mailing list