pythoninheritanceclass-attributes

Why don't my subclass instances contain the attributes from the base class (causing an AttributeError when I try to use them)?


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?


Solution

  • 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.