python - with - typeerror: super() argument 1 must be type, not classobj
Understanding Python super() with__init__() methods (5)
I'm trying to understand
The reason we use
super is so that child classes that may be using cooperative multiple inheritance will call the correct next parent class function in the Method Resolution Order (MRO).
In Python 3, we can call it like this:
class ChildB(Base): def __init__(self): super().__init__()
In Python 2, we are required to use it like this:
Without super, you are limited in your ability to use multiple inheritance:
Base.__init__(self) # Avoid this.
I further explain below.
"What difference is there actually in this code?:"
class ChildA(Base): def __init__(self): Base.__init__(self) class ChildB(Base): def __init__(self): super(ChildB, self).__init__() # super().__init__() # you can call super like this in Python 3!
The primary difference in this code is that you get a layer of indirection in the
super, which uses the current class to determine the next class's
__init__ to look up in the MRO.
I illustrate this difference in an answer at the canonical question, How to use 'super' in Python?, which demonstrates dependency injection and cooperative multiple inheritance.
If Python didn't have
Here's code that's actually closely equivalent to
super (how it's implemented in C, minus some checking and fallback behavior, and translated to Python):
class ChildB(Base): def __init__(self): mro = type(self).mro() # Get the Method Resolution Order. check_next = mro.index(ChildB) + 1 # Start looking after *this* class. while check_next < len(mro): next_class = mro[check_next] if '__init__' in next_class.__dict__: next_class.__init__(self) break check_next += 1
Written a little more like native Python:
class ChildB(Base): def __init__(self): mro = type(self).mro() for next_class in mro[mro.index(ChildB) + 1:]: # slice to end if hasattr(next_class, '__init__'): next_class.__init__(self) break
If we didn't have the
super object, we'd have to write this manual code everywhere (or recreate it!) to ensure that we call the proper next method in the Method Resolution Order!
How does super do this in Python 3 without being told explicitly which class and instance from the method it was called from?
It gets the calling stack frame, and finds the class (implicitly stored as a local free variable,
__class__, making the calling function a closure over the class) and the first argument to that function, which should be the instance or class that informs it which Method Resolution Order (MRO) to use.
Since it requires that first argument for the MRO, using
super with static methods is impossible.
Criticisms of other answers:
super() lets you avoid referring to the base class explicitly, which can be nice. . But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.
It's rather hand-wavey and doesn't tell us much, but the point of
super is not to avoid writing the parent class. The point is to ensure that the next method in line in the method resolution order (MRO) is called. This becomes important in multiple inheritance.
I'll explain here.
class Base(object): def __init__(self): print("Base init'ed") class ChildA(Base): def __init__(self): print("ChildA init'ed") Base.__init__(self) class ChildB(Base): def __init__(self): print("ChildB init'ed") super(ChildB, self).__init__()
And let's create a dependency that we want to be called after the Child:
class UserDependency(Base): def __init__(self): print("UserDependency init'ed") super(UserDependency, self).__init__()
ChildB uses super,
ChildA does not:
class UserA(ChildA, UserDependency): def __init__(self): print("UserA init'ed") super(UserA, self).__init__() class UserB(ChildB, UserDependency): def __init__(self): print("UserB init'ed") super(UserB, self).__init__()
UserA does not call the UserDependency method:
>>> UserA() UserA init'ed ChildA init'ed Base init'ed <__main__.UserA object at 0x0000000003403BA8>
>>> UserB() UserB init'ed ChildB init'ed UserDependency init'ed Base init'ed <__main__.UserB object at 0x0000000003403438>
Criticism for another answer
In no circumstance should you do the following, which another answer suggests, as you'll definitely get errors when you subclass ChildB:
super(self.__class__, self).__init__() # Don't do this. Ever.
(That answer is not clever or particularly interesting, but in spite of direct criticism in the comments and over 17 downvotes, the answerer persisted in suggesting it until a kind editor fixed his problem.)
Explanation: That answer suggested calling super like this:
This is completely wrong.
super lets us look up the next parent in the MRO (see the first section of this answer) for child classes. If you tell
super we're in the child instance's method, it will then lookup the next method in line (probably this one) resulting in recursion, probably causing a logical failure (in the answerer's example, it does) or a
RuntimeError when the recursion depth is exceeded.
>>> class Polygon(object): ... def __init__(self, id): ... self.id = id ... >>> class Rectangle(Polygon): ... def __init__(self, id, width, height): ... super(self.__class__, self).__init__(id) ... self.shape = (width, height) ... >>> class Square(Rectangle): ... pass ... >>> Square('a', 10, 10) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in __init__ TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'
This question already has an answer here:
- What does 'super' do in Python? 6 answers
I'm trying to understand the use of
super(). From the looks of it, both child classes can be created, just fine.
I'm curious to know about the actual difference between the following 2 child classes.
class Base(object): def __init__(self): print "Base created" class ChildA(Base): def __init__(self): Base.__init__(self) class ChildB(Base): def __init__(self): super(ChildB, self).__init__() ChildA() ChildB()
super() lets you avoid referring to the base class explicitly, which can be nice. But the main advantage comes with multiple inheritance, where all sorts of fun stuff can happen. See the standard docs on super if you haven't already.
Note that the syntax changed in Python 3.0: you can just say
super().__init__() instead of
super(ChildB, self).__init__() which IMO is quite a bit nicer. The standard docs also refer to a guide to using super() which is quite explanatory.
Just a heads up... with Python 2.7, and I believe ever since
super() was introduced in version 2.2, you can only call
super() if one of the parents inherit from a class that eventually inherits
object (new-style classes).
Personally, as for python 2.7 code, I'm going to continue using
BaseClassName.__init__(self, args) until I actually get the advantage of using
Super has no side effects
Base = ChildB Base()
works as expected
Base = ChildA Base()
gets into infinite recursion.
There isn't, really.
super() looks at the next class in the MRO (method resolution order, accessed with
cls.__mro__) to call the methods. Just calling the base
__init__ calls the base
__init__. As it happens, the MRO has exactly one item-- the base. So you're really doing the exact same thing, but in a nicer way with
super() (particularly if you get into multiple inheritance later).