Home > Back-end >  Access child's __class__ from a parent's method in Python
Access child's __class__ from a parent's method in Python

Time:09-24

When I run the code below, I get a reference to the parent class A at the end, while I was hoping to refer to the child B.

In [1]: class A:
    ...:     def f():
    ...:         print(__class__)
    ...: 

In [2]: class B(A): pass

In [3]: B.f()
<class '__main__.A'>

I know I can do something like

In [4]: class A:
    ...:     def f(self):
    ...:         print(self.__class__)
    ...: 

In [5]: class B(A): pass

In [6]: B().f()
<class '__main__.B'>

but I'm wondering if there is any way I could drop the extra parentheses.

CodePudding user response:

If you want to be able to call the function on the class as well, not just an instance, and know which class it was called on (and for an instance, the class of the instance it was called on), make the method a classmethod, e.g.:

class A:
    @classmethod
    def f(cls):
        print(cls) 

class B(A):
    pass

The classmethod decorator makes a descriptor that will automatically provide the class object it was called on, whether it was called on the class itself or an instance of the class, and provides the class object as the first argument, rather than an instance. It's almost exclusively used to define alternate constructors (for the purpose of taking some argument format not accepted by __init__, converting from that format to one __init__ accepts, then ending with return cls(converted args here)), but if you're really insistent on:

  1. Using classes for this, and
  2. Not making instances of the classes for whatever reason

it can handle both your use cases (B.f() and B().f()); classmethod strips instance information when called, so the latter behaves identically to the former (they both pass B as the value of cls in f).

print(__class__) alone didn't work because __class__ is intentionally tied to the class the function was defined in, as part of the machinery for making no-arg super() calls work (it uses some pretty awful hacks to make super() behave like it was really called with the first positional argument passed to the function, typically self, plus a __class__ variable smuggled into a closure scope when the class the function was defined in finishes being defined); making __class__ redefine itself based on being called on a subclass would turn all uses of super() into infinite recursion.

  • Related