Parent class rather than self()?

I am looking at some code and I think I know what it is doing but I want to check.

class Enemy(Item):
    def __init__(self):
    self.Awake = False

The reason I am asking is I am teaching A Level python and the exam board supply a ‘skeleton program’ that is used in the exam. This is from an old one from a previous year we are using for practice and the above seems very odd. When I asked why all the methods were using

Feels like the second line is the same as ‘super().init(self):’ but it seems weird. If this is the case why would Item be used instead? My guess is because it is code that was converted from Jave. Does anyone have any thoughts?

You lost some of the indent when you posted the code.

My guess is the original is from a python2 world.
It should be super().__init__() for python 3 world.

P.s. _ becomes markup unless quoted as preformatted text.


class Enemy(Item):

    def __init__(self):
        Item.__init__(self)   # Name superclass explicitly, pass `self`
        self.Awake = False


class Enemy(Item):

    def __init__(self):
        super().__init__()   # Reference superclass generically, omit self
        self.Awake = False

Simple comparison:

class Item:

    def __init__(self):

        print("Inside the class Item '__init__' constructor.")

    def misc(self):

        print('Called the misc() method.')

class Enemy(Item):

    def __init__(self):
        Item.__init__(self)   # Name superclass explicitly, pass `self`
        self.Awake = False

        super().misc()        # Call the method from the superclass

obj = Enemy()

My sympathies. The traditional education system is not well suited for teaching programming. I think this was at least subconsciously a reason why I never pursued that career path. How you handle this is up to you, but it will depend on who you report to and what their expectations (and, perhaps accreditation requirements?) are. But I can at least explain about the code.

You are correct that super().__init__() - without a self being passed explicitly; it would be wrong to add it - accomplishes the same thing in this simple example. The explicit call is looking the method up directly in the Item class, which in Python 3 finds a perfectly ordinary function expecting self to be passed explicitly (it’s listed as a parameter, after all).[1] On the other hand, super() creates a deeply magical object[2]:, but __init__ is a method on that object, being called in the normal way.

The super approach shines when multiple inheritance is used - it allows for setting up a cooperative system for initializing all the bases. Historically, if you inherited from two classes, it wouldn’t necessarily be clear if you should call __init__ on both, or else which one. Generally, ideally, you’d want every class “above” the current one in the hierarchy to get its __init__ called exactly once. Using super solves the “diamond inheritance problem” and otherwise simplifies matters.

For more information I recommend this talk by @rhettinger (of the core dev team):

Or this earlier blog post which I believe was the basis for the talk:

As for why your material shows a direct call instead, we can only really speculate. “converted from Java” is certainly a possibility. Simple ignorance seems quite likely to me. super was added to the language all the way back in 2.2, released at the end of 2001, so “the material is too old” seems a bit unlikely. However, the original implementation of super was not as convenient[3] and had other limitations[4], so it was (from what I can tell) much less used - contributing to general ignorance of the feature.

  1. In Python 2, there were separate “unbound method” and “bound method” objects created by the system, but the semantics were much the same. In Python 3, we only have methods (what were “bound methods”) and functions (since distinguishing “unbound methods” from ordinary functions turned out to be completely useless). ↩︎

  2. Both the object and its manner of construction are magical. In the most common use, the super object stores a reference to an instance and its class, and when any method is called on it, it attempts to forward that call to the corresponding instance or class method. (It can be used in other ways as well; see help(super), or the documentation, for details.) When super() is used inside a method, the stack trace is inspected to figure out what the instance and the class are, so they can be used in the subsequent magic method call. ↩︎

  3. The magic to allow super to be called without arguments was figured out during Python 3.0 development, and back-ported to 2.6 and 2.7. Up through 2.5 one would have to write super(Enemy, self).__init__(), which is annoying and looks redundant - and if I’m thinking straight, doesn’t actually support the cooperative multiple inheritance thing fully either. ↩︎

  4. Python 2.x had a distinction between “new-style” classes, which always have object at the root of the inheritance hierarchy, and “old-style” classes, which don’t. The super functionality required new-style classes to work. So if Item had been declared as class Item:, then super wouldn’t work in the derived classes. You would have to write class Item(object): instead. To my understanding, this happened because the “old style” was the only option in 1.x, and 2.0 was trying not to break as much backwards compatibility as 3.0 ended up doing :wink: ↩︎

Usually yes, you’d just use super().__init__(). Shorter, clearer and
less fragile (eg if the class inheritance was modified).

The only time I’d explicitly enumerate some superclass is with multiple
inheritance where I wanted to call the inits of a couple of superclasses
specially. Contrived example:

 class C(A,B):

     def __init__(self, aopt, bopt):
         A.__init__(self, aopt)
         B.__init__(self, bopt)

i.e. where I’m parcelling out specific arguments to particular
superclasses. This is pretty uncommon.

1 Like