A class body executes as an unoptimized function, which uses a locals mapping instead of fast locals. The locals mapping ultimately gets copied as the __dict__
of the instantiated type object. A class body also creates a __class__
closure for methods that either use super()
without arguments or that explicitly reference __class__
. The cell
object for the closure gets stored as a local variable named __classcell__
, which gets processed by the LOAD_BUILD_CLASS
opcode. Thus manually assigning to __classcell__
isn’t supported. For example:
>>> class A:
... __classcell__ = 1
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __classcell__ must be a nonlocal cell, not <class 'int'>
A name that’s assigned to in a class body is implicitly declared local (i.e. to use LOAD_NAME
and STORE_NAME
), unless it’s explicitly declared global
(i.e. to use LOAD_GLOBAL
and STORE_GLOBAL
) or nonlocal
(i.e. to use LOAD_CLASSDEREF
and STORE_DEREF
).
Referencing an unoptimized local variable via LOAD_NAME
has always been a bit peculiar in Python, because it falls back on checking globals and builtins. It’s set up this way to allow temporarily shadowing a global or builtin name. For example:
>>> x = 1
>>> class A:
... print('x, 1st:', x)
... x = 2
... print('x, 2nd:', x)
... locals()['x'] = 3
... print('x, 3rd:', x)
... del x
... print('x, 4th:', x)
...
x, 1st: 1
x, 2nd: 2
x, 3rd: 3
x, 4th: 1
Because locals in a class body is just a mapping, a local variable can be assigned dynamically, such as locals()['x'] = 3
.
For a free variable from an outer function scope that’s referenced in a class body, the LOAD_CLASSDEREF
opcode tries to capture the dynamism and non-local fallback of the classic LOAD_NAME
opcode. However, I think it’s a mistake that the compiler doesn’t switch to using the regular LOAD_DEREF
opcode when a variable in the class body is explicitly declared nonlocal
. The latter would be consistent with how the compiler switches to using LOAD_GLOBAL
instead of LOAD_NAME
when a variable in a class body is declared global
. For example:
>>> x = 1
>>> class A:
... global x
... print('x, 1st:', x)
... locals()['x'] = 2 # ignored because x is global
... print('x, 2nd:', x)
...
x, 1st: 1
x, 2nd: 1