Multiple Constructors while Subclassing


Update 2: After extensive back-and-forth with Gregg (who is a real programmer), I’ve come to the conclusion that my original post was, in fact, relevant. The original post stands.




While the backlash code and thoughts presented in Update 1 are valid, they do not address my specific issue. My case requires multiple constructors because of non-unique variable naming conventions:

class Base :
    def __init__ (self) :
        self.serial_number = '123' # Default Base sn

class Component :
    def __init__ (self):
        self.serial_number = '456' # Default Component sn

class Derived (Base) :
    def __init__ (self):
        self.component = Component()
        Base.__init__(self)

Which leads to:

>>> from test import *
>>> instance = Derived()
>>> instance.serial_number
'123'
>>> instance.component.serial_number
'456'
>>>



Update 1: The entire content of the original post (after the jump) is completely unnecessary. Witness:

class Base :
    default_attribute = 23

class Derived (Base) :
    instance_attribute = 31

I fuckin’ hate Occam’s Razor.

>>> a = Derived()
>>> dir(a)
['__doc__', '__module__', 'default_attribute', 'instance_attribute']
>>> a.default_attribute
23
>>> a.instance_attribute
31
>>> 

As resident IdiotBoy, I concede this entire exercise is crap… unless you’re talking about calling different (required) methods in both superclass and subclass. Wait… how is this shit relevant at all?

Original Post:
I learned something cool about Python classes today. Now, I’ve been using them for a long time, yet never ran into this problem. Thankfully, I work with Super Python Guru Guy, and he hooked me up. This may seem obvious to other Python coders, but I never thought of it…



Problem: I have two classes… one is the base class (interestingly named “Base”)… and one is the class I’ll actually use, which subclasses “Base”, named “Derived”. Both of these classes have constructor methods (the __init__() functions) that get called whenever an instance of the Derived class is created. Check it:

class Base :
    def __init__ (self) :
        self.default_attribute = 23

class Derived (Base) :
    def __init__ (self) :
        self.instance_attribute = 31

So, when I say “create an instance of the class Derived”, here’s what happens:

  1. Look at the class definition for Derived.
  2. Notice that it is based upon (subclasses) Base.
  3. Look at the class definition for Base.
  4. Call the __init__() function, which sets a default attribute (23).
  5. Go back to the class definition for Derived.
  6. Call its __init__() function, which sets another attribute (31).

Now, you’d think that your new instance of Derived has two attributes (23 and 31). But you’d be wrong. Check it:

>>> from test import *
>>> instance = Derived()
>>> instance.__dict__ 
{'instance_attribute': 31}
>>>

WTF, right? Well, as you’d expect with classes, any function you define in the class you instantiate (here, Derived) will override that same function in the class you subclass (Base), if that function exists. Since both of these classes have an __init__() function, the one in Derived overrides the one in Base… which is why we’re left with only 31. It’s as if the __init__() function of Base was never called… which it actually wasn’t!



Jeez, this was killing me! How the hell can I get both to work? Simple, explicitly call the __init__() function of Base that got overridden:

class Base :
    def __init__ (self) :
        self.default_attribute = 23

class Derived (Base) :
    def __init__ (self) :
        self.instance_attribute = 31
        Base.__init__(self)

Does it work?

>>> from test import *
>>> instance = Derived()
>>> instance.__dict__ 
{'instance_attribute': 31, 'default_attribute': 23}
>>>

Sure does! Pretty kick ass.

Advertisements

2 thoughts on “Multiple Constructors while Subclassing

  1. Without interfaces (or, does it have them now?) doesn’t this make things kinda fragile? You could create a subclass that somehow gets the wrong behavior, based on calling the superclass init, non?

  2. Skipping the interfaces part (b/c I know jackshit about that)… I guess you could inadvertently do what you describe–if you knew just enough to fuck yourself–which I suppose makes the stuff I describe inherently fragile.

    But you’d have to purposefully and explicitly (inadvertently?) call the superclass init in your derived class… which isn’t the normal case.

    Normally, you’d subclass Base because you want a starting point from which to work. If not, then you’d write a new class altogether and subclass nothing.

    If Base has a few useful functions or attributes (even outside the init), then you’d subclass it and override properties and functions as needed. By default (as hinted in my post), the subclass overrides the exactly-named attributes and functions/methods of the superclass.

    Generally, this is a good thing, b/c it guards against the kind of unexpected shit you’re talking about.

    In my case-specific situation, though, I don’t want to explicitly set the same variables to the same default values in every subclass… I want that shit to happen automatically, and cascade down the subclassing action, where each subclass only adds to the growing list of attributes. Otherwise, I wouldn’t be worried about it, and wouldn’t have defined the init in any of my derived classes.

Comments are closed.