Classes have a special opcode, LOAD_NAME, that allows for
1
2
3
4
5
6
>>> x = 42
>>> class A :
... x = x
...
>>> A . x
42
which would fail in a function
1
2
3
4
5
6
7
8
>>> def f ():
... x = x
...
>>> f ()
Traceback ( most recent call last ):
File "<stdin>" , line 1 , in < module >
File "<stdin>" , line 2 , in f
UnboundLocalError : local variable 'x' referenced before assignment
LOAD_NAME is pretty dumb, it looks into the local namespace and if that
lookup fails falls back to the global namespace. Someone probably thought “I
can do better”, and reused the static name lookup for nested functions for
names that occur only on the right-hand side of assignments in a class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> x = "global"
>>> def foo ():
... x = "local"
... class A :
... x = x
... return A
...
>>> def bar ():
... x = "local"
... class A :
... y = x
... return A
...
>>> foo () . x
'global'
>>> bar () . y
'local'
Now let’s have a glimpse at the bytecode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> import dis
>>> foo . func_code . co_consts
( None , 'local' , 'A' , < code object A at 0x7ffe311bdb70 , file "<stdin>" , line
3 > , ())
>>> dis . dis ( foo . func_code . co_consts [ 3 ])
3 0 LOAD_NAME 0 ( __name__ )
3 STORE_NAME 1 ( __module__ )
4 6 LOAD_NAME 2 ( x )
9 STORE_NAME 2 ( x )
12 LOAD_LOCALS
13 RETURN_VALUE
>>> bar . func_code . co_consts
( None , 'local' , 'A' , < code object A at 0x7ffe311bd828 , file "<stdin>" , line
3 > , ())
>>> dis . dis ( bar . func_code . co_consts [ 3 ])
3 0 LOAD_NAME 0 ( __name__ )
3 STORE_NAME 1 ( __module__ )
4 6 LOAD_DEREF 0 ( x )
9 STORE_NAME 2 ( y )
12 LOAD_LOCALS
13 RETURN_VALUE
Source