28

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?

glglgl
  • 81,640
  • 11
  • 130
  • 202
wim
  • 266,989
  • 79
  • 484
  • 630

2 Answers2

28

This is a weird interaction in Python 3's implementation of no-argument super. An access to super 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 name super 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 of super. However, if you try to access __class__ yourself, all the code looking for __class__ sees it and thinks it should do the super handling!

Here's where it adds the name __class__ to the symbol table if it sees super:

case Name_kind:
    if (!symtable_add_def(st, e->v.Name.id,
                          e->v.Name.ctx == Load ? USE : DEF_LOCAL))
        VISIT_QUIT(st, 0);
    /* Special-case super: it counts as a use of __class__ */
    if (e->v.Name.ctx == Load &&
        st->st_cur->ste_type == FunctionBlock &&
        !PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) {
        if (!GET_IDENTIFIER(__class__) ||
            !symtable_add_def(st, __class__, USE))
            VISIT_QUIT(st, 0);
    }
    break;

Here's drop_class_free, which sets ste_needs_class_closure:

static int
drop_class_free(PySTEntryObject *ste, PyObject *free)
{
    int res;
    if (!GET_IDENTIFIER(__class__))
        return 0;
    res = PySet_Discard(free, __class__);
    if (res < 0)
        return 0;
    if (res)
        ste->ste_needs_class_closure = 1;
    return 1;
}

The compiler section that checks ste_needs_class_closure and creates the implicit cell:

if (u->u_ste->ste_needs_class_closure) {
    /* Cook up an implicit __class__ cell. */
    _Py_IDENTIFIER(__class__);
    PyObject *tuple, *name, *zero;
    int res;
    assert(u->u_scope_type == COMPILER_SCOPE_CLASS);
    assert(PyDict_Size(u->u_cellvars) == 0);
    name = _PyUnicode_FromId(&PyId___class__);
    if (!name) {
        compiler_unit_free(u);
        return 0;
    }
    ...

There's more relevant code, but it's too much to include all of it. Python/compile.c and Python/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__:

class Foo:
    def f(self):
        __class__ = 3
        super()

Foo().f()

Output:

Traceback (most recent call last):
  File "./prog.py", line 6, in <module>
  File "./prog.py", line 4, in f
RuntimeError: super(): __class__ cell not found

The assignment to __class__ means __class__ is a local variable instead of a closure variable, so the closure cell super() needs isn't there.

def f():
    __class__ = 2
    class Foo:
        def f(self):
            print(__class__)

    Foo().f()

f()

Output:

<class '__main__.f.<locals>.Foo'>

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.

Cristian Ciupitu
  • 18,164
  • 7
  • 46
  • 70
user2357112 supports Monica
  • 215,440
  • 22
  • 321
  • 400
  • Thanks for answering the *how*. Now another question is the *why*: why is this `__class__` reference not *always* available but only when `__class__` or `super()` is used? Higher stack frames might need to access it to get the calling class in lower stack frames. For instance [here](https://stackoverflow.com/questions/63402581/how-to-get-the-class-from-which-a-method-was-called). – Maggyero Aug 17 '20 at 11:24
  • 1
    @Maggyero: Extra closure variables carry a small but consistent per-call performance penalty. Making every single user-defined method in all Python code pay that penalty just to enable more weird anti-pattern introspection wouldn't be worthwhile. Plus, to really work the way you're looking for, it'd have to be inherited by nested scopes, slowing down comprehensions and generator expressions too. – user2357112 supports Monica Aug 18 '20 at 10:55
  • Okay, thanks for your input. So you would have rejected [PEP 3130](https://www.python.org/dev/peps/pep-3130/) for performance reasons (the PR was actually rejected but I don’t know the reasons)? – Maggyero Aug 18 '20 at 11:02
  • @Maggyero: PEP 3130 doesn't propose what you're looking for. Methods that don't use `__class__` would not have been required to carry a `__class__` reference under PEP 3130 semantics. – user2357112 supports Monica Aug 18 '20 at 11:07
17

https://docs.python.org/3/reference/datamodel.html#creating-the-class-object

__class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.

user2683246
  • 2,966
  • 26
  • 28
  • This is *documented*? Well, now I feel silly about digging through the source for this. This is a better answer than mine. – user2357112 supports Monica May 03 '16 at 01:18
  • 2
    Neither answer really explains to me why the reference needs to be created when super wasn't used. – wim May 03 '16 at 01:44
  • 1
    @wim: It doesn't need to be created. It just is anyway. I'm guessing the implementation just happened to work that way and then they documented it because it wasn't a big enough deal to be worth changing, rather than this being by design. – user2357112 supports Monica May 03 '16 at 02:00
  • @user2357112 I'll have to disagree - your answer is much better. The fact that it's documented doesn't tell us anything about how the magic trick works, and it only offers an unconvincing explanation for why it's there at all. – wim Mar 15 '17 at 20:09