50

I have this base class and subclass:

class Event:
    def __init__(self, sr1=None, foobar=None):
        self.sr1 = sr1
        self.foobar = foobar


# Event class wrappers to provide syntatic sugar
class TypeTwoEvent(Event):
    def __init__(self, level=None):
        self.sr1 = level

Later on, when I try to check the foobar attribute of a TypeTwoEvent instance, I get an exception. For example, testing this at the REPL:

>>> event = TypeTwoEvent()
>>> event.foobar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'TypeTwoEvent' object has no attribute 'foobar'

I thought that the base class attributes would be inherited by the subclass and that creating an instance of a subclass would instantiate the base class (and thus invoke its constructor). Therefore, I expected the foobar attribute value to be defaulted to None.

Why do TypeTwoEvent instances not have a foobar attribute, even though Event instances do?

1
  • 3
    As said below, you need to explicitly indicate that you want the superclasses to initialise as well. But take care: if you have any multiple inheritance then making this happen becomes very delicate. Commented Apr 22, 2012 at 14:12

6 Answers 6

49

The subclass should be:

class TypeTwoEvent(Event):    
    def __init__(self, level=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sr1 = level

Because __init__ is overridden, the base class' __init__ code will only run if it is explicitly requested.

Despite its strange name, __init__ is not specially treated. It gets called automatically after the object is created; but otherwise it's an ordinary method, and ordinary inheritance rules apply.

super().__init__(arguments, that, go, to, parents)

is the syntax to call the parent version of the method. Using *args and **kwargs allows us to catch additional arguments passed to __init__ and pass them to the parent method; this way, when a TypeTwoEvent is created, a value can be specified for the foobar, along with anything else specific to the base class.

Sign up to request clarification or add additional context in comments.

Comments

4

You're overriding the constructor (__init__) of the parent class. To extend it, you need to explicitly call the constructor of the parent with a super() call.

class TypeTwoEvent(Event):
    def __init__(self, level=None, **kwargs):
        # the super call to set the attributes in the parent class
        super().__init__(**kwargs)
        # now, extend other attributes
        self.sr1 = level
        self.state = STATE_EVENT_TWO

Note that the super call is not always at the top of the __init__ method in your sub-class. Its location depends on your situation and logic.

1 Comment

"Its location depends on your situation and logic." This is true, but it would be more helpful to explain the consequences of writing it later instead of at the top - what problems can be solved (or created) this way.
2

When the instance is created, its __init__ method is called. In this case, that is TypeTwoEvent.__init__. Superclass methods will not be called automatically because that would be immensely confusing.

You should call Event.__init__(self, ...) from TypeTwoEvent.__init__ (or use super, but if you're not familiar with it, read up on it first so you know what you're doing).

Comments

1

You need to call the __init__ method of the base class from the __init__ method of the inherited class.

See here for how to do this.

Comments

0

I've had the same problem, but in my case I put super().__init__() on the bottom of my derived class and that's why it doesn't work. Because I tried to use attributes that are not initialized.

4 Comments

I have the same problem did you find a solution by any chance?
Hey @enesislam, yes, as far as I remember, it was something with trying to use attributes from parent class, but parent weren't initialized by calling parent constructor before.
Hey! Thank you for your answer. I solved my problem. I had self.parent in the parent class. When I try to use super().__init__() in child class it said the object has no attribute. So I have tried to use like super().__init__(parent=self.parent) but the result was the same. The true way to solve this problem is to send that parent value by args. So it should be: super().__init__(*args, **kwargs)
@enesislam Good catch! Glad you managed to get it solved!
0

None of the above fixed my problem. Apparently sometimes Python doesn't like the subclass to inherit @properties. See this screenshot:

enter image description here

parent_graph used to be an @property and the same analogous error you see displayed in the picture occured. Now I made it into a regular method. So now it gets past that error and moves onto the next uninherited @property which is ambient_space. So now you can imagine what I have to do: take away @property's in practically my whole project.

Arrow is using single inheritance from base class Base which in turn singly inherits from QGraphicsObject. I am definitely calling the super().__init__() appropriately.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.