super and __init__

N

Noah

Am I the only one that finds the super function to be confusing?

I have a base class that inherits from object.
In other words new style class:

class foo (object):
def __init__ (self, arg_A, arg_B):
self.a = arg_A
self.b = arg_B
# Do I need to call __init__ on "object" base class?

class bar (foo):
def __init__ (self, arg_Z):
self.z = "Z" + arg_Z
foo.__init__(self, 'A', arg_Z) # this is the old-style
class way
def __str__ (self):
return self.a + self.b + self.z

I don't know how people will use the "bar" class
in terms of inheritance. I don't want to lock anyone
out of multiple inheritance, but I'd like to have to
worry about it as little as possible. From what I've
read using the old style of calling the
base class __init__ can cause conflicts
if the class is later part of a diamond relationship.

I just want "bar" to initialize the properties that it add
to the base class and to have it tell the base class to
initialize the inherited properties. The old way seemed
clear enough; although, a bit ugly. The super function
seems like it would make this more clear, but
it doesn't (to me).

Is there a "just do this" answer for 90% of the use cases?

Yours,
Noah
 
J

Jason

Noah said:
Am I the only one that finds the super function to be confusing?

I have a base class that inherits from object.
In other words new style class:

class foo (object):
def __init__ (self, arg_A, arg_B):
self.a = arg_A
self.b = arg_B
# Do I need to call __init__ on "object" base class?

class bar (foo):
def __init__ (self, arg_Z):
self.z = "Z" + arg_Z
foo.__init__(self, 'A', arg_Z) # this is the old-style
class way
def __str__ (self):
return self.a + self.b + self.z

I don't know how people will use the "bar" class
in terms of inheritance. I don't want to lock anyone
out of multiple inheritance, but I'd like to have to
worry about it as little as possible. From what I've
read using the old style of calling the
base class __init__ can cause conflicts
if the class is later part of a diamond relationship.

I just want "bar" to initialize the properties that it add
to the base class and to have it tell the base class to
initialize the inherited properties. The old way seemed
clear enough; although, a bit ugly. The super function
seems like it would make this more clear, but
it doesn't (to me).

Is there a "just do this" answer for 90% of the use cases?

Yours,
Noah

As far as I can tell, the best way to use super() with an __init__
function is to stick to a rigid function signiture. This means, all
__init__'s must either have the same functions, accept parameters in
the same order (and handle excess parameters through the *args
mechanism), or use keyword arguments (using the **keyargs mechanism).

So, use one of the following for all your classes in the hierarchy:
def __init__(self, arg1, arg2): # No subclass can add or remove
arguments
pass

def __init__(self, arg1, arg2, *args):
# Subclasses can add arguments, but cannot remove or have a
different
# argument order. The instances must be created with all possible
parameters.
pass

def __init__(self, arg1, arg2, **keyargs):
# Subclasses can add or remove arguments, and order doesn't matter.
# The instances must be created with all possible keyword
parameters.
pass

Unfortunately, I don't see a way of avoiding this problem with super().
Any such super command resolves in the mro order. Since the mro order
invoked at a certain class can change depending on its subclasses,
there's no way for the class to predict at design time what super() is
going to return. You can predict what will be returned with your class
hierarchy, but another programmer can create a multiple-inheritence
class that will change the result.

Explicitly calling the base class is much easier, but a given class
method can be called multiple times in that case.

I do wish there was a way to kinda combine the two methods: Explicitly
call the super-classes, but do so that each super-method can get called
one or no times. Unfortunately, I haven't (yet) found a way to do so
that can resolve things right.

That's not to say that there isn't a better way. I'm sure that the
Python developers had a devil of a time working on this thing.

--Jason
 
N

Noah

Jason said:
Unfortunately, I don't see a way of avoiding this problem with super().
Any such super command resolves in the mro order. Since the mro order
invoked at a certain class can change depending on its subclasses,
there's no way for the class to predict at design time what super() is
going to return. You can predict what will be returned with your class
hierarchy, but another programmer can create a multiple-inheritence
class that will change the result.

Explicitly calling the base class is much easier, but a given class
method can be called multiple times in that case.

If I know that multiple calls to my base class __init__ is harmless
and multiple calls to my derrived class __init__ is harmless then
is it best to just go ahead and use the old style of explicitly calling
the __init__? I'm just worried about using the old style base __init__
call
with new style objects.

Since inheritance is so fundemental to an object oriented language
it's bad that Python makes it so easy to get the constructor wrong.

Yours,
Noah
 
D

Duncan Booth

Jason said:
As far as I can tell, the best way to use super() with an __init__
function is to stick to a rigid function signiture. ....
Unfortunately, I don't see a way of avoiding this problem with super().

An easy way to avoid changing the method signature is to use a multi-stage
construction. So if your class hierarchy uses:

def __init__(self, foo, bar):
super(ThisClass, self).__init__(foo, bar)
... whatever ...

and you are adding another class to the hierarchy, but that needs a 'baz'
as well, don't change the signature for __init__, add another method:

def set_baz(self, baz): ...

Then at the point you construct the objects you can call:

x = DerivedClass(foo, bar)
x.set_baz(baz)

If set_baz isn't called then you either use a default value or throw an
error when something depends on it having been set.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top