Quantum Mechanic
Quantum Mechanic

Reputation: 662

What class is this code defined in?

In a method in a class, I need to call super() of the class where the method is defined, not the class of the object. I specifically care about Python 2.x, but 3.x solutions are welcome.

I want to have generic boilerplate that can be used throughout a project, where one class inherits from another, to an unknown depth. To avoid recursion errors, I need to call the parent class of the current class where the code is defined, not the parent of the self object. There are a number of places in each module where the current class name is referenced, as many methods are extended, and each extended method may call the parent class method.

Here's demo that fails:

#!/usr/bin/env python

from inspect import stack

class Foo(object):
    def __init__(self):
        print "Foo.__init__()"
        print "file: %s" % __file__
        print "class: %s" % self.__class__.__name__
        print "stack: %s" % stack()[0][3]
        print

class Bar(Foo):
    def __init__(self):
        print "Bar.__init__()"
        super(type(self), self).__init__()

class Baz(Bar):
    pass


foo = Foo()
bar = Bar()
baz = Baz()

Here is the result:

Foo.__init__()
file: ./class.py
class: Foo
stack: __init__

Bar.__init__()
Foo.__init__()
file: ./class.py
class: Bar
stack: __init__

Bar.__init__()
Bar.__init__()

# snip several hundred lines

Bar.__init__()
Bar.__init__()
Traceback (most recent call last):
  File "./class.py", line 24, in <module>
    baz = Baz()
  File "./class.py", line 16, in __init__
    super(type(self), self).__init__()

# snip several hundred lines

  File "./class.py", line 16, in __init__
    super(type(self), self).__init__()
  File "./class.py", line 16, in __init__
    super(type(self), self).__init__()
RuntimeError: maximum recursion depth exceeded while calling a Python object

Upvotes: 1

Views: 59

Answers (1)

MSeifert
MSeifert

Reputation: 152667

As you already found out using type(self) leads to recursions because Baz.__init__ is directed to Bar.__init__ but in there you want to call super(Baz, self).__init__ which is again Bar.__init__ so you end up with an infinite recursion.

Generally it's convention that you only call the parent without knowing on which class-instance you've called it because otherwise you'll need to really know and fix your MRO. You can always find out which child-class called the method with self.__class__ or self.__class__.__name__

Resolving it is quite simple: Replace your super-calls with:

super(Bar, self).__init__()

in python3 to avoid these (mostly unnecessary) hardcoding of classes in super it's also possible to use:

super().__init__()

That will always call the method on the next parent class that implements it. There are really seldom cases where the parent class of your current class is not the next parent class resolved by the super call.

The output then becomes (missing __file__):

Foo.__init__()
class: Foo
stack: __init__

Bar.__init__()
Foo.__init__()
class: Bar
stack: __init__

Bar.__init__()
Foo.__init__()
class: Baz
stack: __init__

Upvotes: 1

Related Questions