There's a surprise here:
>>> class B:
... print(locals())
... def foo(self):
... print(locals())
... print(__class__ in locals().values())
...
{'__module__': '__main__', '__qualname__': 'B'}
>>> B().foo()
{'__class__': <class '__main__.B'>, 'self': <__main__.B object at 0x7fffe916b4a8>}
True
It seems like the mere mention of __class__
is explicitly checked by the parser? Otherwise we should get something like
NameError: name '__class__' is not defined
Indeed, if you modify to only check the key instead, i.e. check for '__class__' in locals()
, then we only have self
in scope as expected.
How does it happen that this variable gets magically injected into scope? My guess is this is something to do with super
- but I didn't use super
, so why does the compiler create an implicit closure reference here if it isn't needed?
https://docs.python.org/3/reference/datamodel.html#creating-the-class-object
This is a weird interaction in Python 3's implementation of no-argument
super
. An access tosuper
in a method triggers the addition of a hidden__class__
closure variable referring to the class that defines the method. The parser special-cases a load of the namesuper
in a method by also adding__class__
to the method's symbol table, and then the rest of the relevant code all looks for__class__
instead ofsuper
. However, if you try to access__class__
yourself, all the code looking for__class__
sees it and thinks it should do thesuper
handling!Here's where it adds the name
__class__
to the symbol table if it seessuper
:Here's
drop_class_free
, which setsste_needs_class_closure
:The compiler section that checks
ste_needs_class_closure
and creates the implicit cell:There's more relevant code, but it's too much to include all of it.
Python/compile.c
andPython/symtable.c
are where to look if you want to see more.You can get some weird bugs if you try to use a variable named
__class__
:Output:
The assignment to
__class__
means__class__
is a local variable instead of a closure variable, so the closure cellsuper()
needs isn't there.Output:
Even though there's an actual
__class__
variable in the enclosing scope, the special-casing of__class__
means you get the class instead of the enclosing scope's variable value.