Should one always add super().__init__() to the __init__?

R

Ramchandra Apte

Should one always add super().__init__() to the __init__? The reason for this is the possibility of changing base classes (and forgetting to update the __init__).
 
R

Ramchandra Apte

Should one always add super().__init__() to the __init__? The reason for this is the possibility of changing base classes (and forgetting to update the __init__).

This is my first post so I may be breaching nettique.
 
I

Ian Kelly

Should one always add super().__init__() to the __init__? The reason for this is the possibility of changing base classes (and forgetting to update the __init__).

As long as the class and its subclasses only use single inheritance,
it makes little difference, so if you think it will reduce the
maintenance burden, I would say go for it.
 
S

Steven D'Aprano

Should one always add super().__init__() to the __init__? The reason for
this is the possibility of changing base classes (and forgetting to
update the __init__).

No. Only add code that works and that you need. Arbitrarily adding calls
to the superclasses "just in case" may not work:



py> class Spam(object):
.... def __init__(self, x):
.... self.x = x
.... super(Spam, self).__init__(x)
....
py> x = Spam(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
TypeError: object.__init__() takes no parameters
 
D

Devin Jeanpierre

No. Only add code that works and that you need. Arbitrarily adding calls
to the superclasses "just in case" may not work:



py> class Spam(object):
... def __init__(self, x):
... self.x = x
... super(Spam, self).__init__(x)
...
py> x = Spam(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
TypeError: object.__init__() takes no parameters

That's a good thing. We've gone from code that doesn't call the
initializer and leaves the object in a potentially invalid state
(silently!), to code that calls the initializer and then fails
(loudly).

-- Devin
 
C

Chris Angelico

No. Only add code that works and that you need. Arbitrarily adding calls
to the superclasses "just in case" may not work:

py> class Spam(object):
... def __init__(self, x):
... self.x = x
... super(Spam, self).__init__(x)
...
py> x = Spam(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __init__
TypeError: object.__init__() takes no parameters

That's because you're subclassing something that doesn't take
parameters and giving it parameters. Of course that won't work. The
normal and logical thing to do is to pass on only the parameters that
you know the parent class expects... but that implies knowing the
parent, so it's kinda moot.

ChrisA
 
P

Piet van Oostrum

Chris Angelico said:
That's because you're subclassing something that doesn't take
parameters and giving it parameters. Of course that won't work. The
normal and logical thing to do is to pass on only the parameters that
you know the parent class expects... but that implies knowing the
parent, so it's kinda moot.

It is not necesarily calling the parent class. It calls the initializer
of the next class in the MRO order and what class that is depends on the
actual multiple inheritance structure it is used in, which can depend
on subclasses that you don't know yet. This makes it even worse.
 
R

Ramchandra Apte

No. Only add code that works and that you need. Arbitrarily adding calls

to the superclasses "just in case" may not work:







py> class Spam(object):

... def __init__(self, x):

... self.x = x

... super(Spam, self).__init__(x)

...

py> x = Spam(1)

Traceback (most recent call last):

File "<stdin>", line 1, in <module>

File "<stdin>", line 4, in __init__

TypeError: object.__init__() takes no parameters

I forgot something:
I meant super().__init__() or similar
 
S

Steven D'Aprano

I forgot something:
I meant super().__init__() or similar

What about it? Please try to remember that we can't read your mind and
don't know what you are thinking, we can only work from what you put in
writing.

There is no difference between super(Class, self).__init__ and
super().__init__ except that the second version only works in Python 3.
 
S

Steven D'Aprano

That's because you're subclassing something that doesn't take parameters
and giving it parameters. Of course that won't work. The normal and
logical thing to do is to pass on only the parameters that you know the
parent class expects... but that implies knowing the parent, so it's
kinda moot.

Which is exactly my point -- you can't call the superclass "just in case"
it changes, because you don't know what arguments the new superclass or
classes expect. You have to tailor the arguments to what the parent
expects, and even whether or not you have to call super at all.[1]

super() is not some magic command "don't bother me with the details, just
make method overriding work". You have to actually think about what you
are overriding. You can't expect to take a class that inherits from dict
and change it to inherit from collections.defaultdict and have super
magically sort out the differences in __init__.

The usual advise given for using super is:

* the method being called by super() needs to exist
* the caller and callee need to have a matching[2] argument signature
* and every occurrence of the method needs to use super()

If all three conditions apply, then yes, you should use super. Otherwise,
perhaps not.

For further discussion and practical examples, see:

http://rhettinger.wordpress.com/2011/05/26/super-considered-super/


For a contrary argument, or at least a look at how NOT to use super, see:

https://fuhm.net/super-harmful/

which makes the mistake of blaming super() for mistakes made by people
who don't use it correctly. Note that the author has back-peddled from
his original argument that super was actively harmful to a less
provocative argument that "you can't use super" (except you actually can:
if you read past the first paragraph, the author tells you exactly what
you need to do to use super correctly).




[1] You *should* call super, unless you have an excellent reason not to,
so that your class doesn't break multiple-inheritance. But you need to do
so with care making sure that the argument signatures are designed for
cooperative use of super.

[2] Matching in this case does not necessarily mean identical.
 
S

Steven D'Aprano

It is not necesarily calling the parent class. It calls the initializer
of the next class in the MRO order and what class that is depends on the
actual multiple inheritance structure it is used in, which can depend on
subclasses that you don't know yet. This makes it even worse.

I don't quite follow you here. It sounds like you are saying that if you
have these classes:

# pre-existing classes
class A(object): pass
class B(object): pass

# your class
class C(A, B): pass

and somebody subclasses A or B, the MRO of C will change. That is not
actually the case as far as I can see.
 
C

Chris Angelico

Which is exactly my point -- you can't call the superclass "just in case"
it changes, because you don't know what arguments the new superclass or
classes expect. You have to tailor the arguments to what the parent
expects, and even whether or not you have to call super at all.[1]

super() is not some magic command "don't bother me with the details, just
make method overriding work". You have to actually think about what you
are overriding.

Yeah. Far as I'm concerned, subclassing should *always* involve
knowing the parent class. You needn't concern yourself with its
implementation (I can subclass dict without caring about the details
of hash randomization), but you have to be aware of its interface. And
if you change the base class without changing your method chaining,
you'd better be changing to a new base class that's equivalent to the
old one.

The advantage of super() is that you can substitute a subclass of X as
a new base class without changing anything. But you need to be sure
that the replacement base class obeys the Liskov Substitution
Principle.

ChrisA
 
R

Ramchandra Apte

What about it? Please try to remember that we can't read your mind and

don't know what you are thinking, we can only work from what you put in

writing.



There is no difference between super(Class, self).__init__ and

super().__init__ except that the second version only works in Python 3.

When I said "super().__init__()" it could have been "super().__init__(size+67)" or whatever arguments are needed for __init__
 
I

Ian Kelly

I don't quite follow you here. It sounds like you are saying that if you
have these classes:

# pre-existing classes
class A(object): pass
class B(object): pass

# your class
class C(A, B): pass

and somebody subclasses A or B, the MRO of C will change. That is not
actually the case as far as I can see.

The MRO of C will not change, but the class that follows C may be
different in the MRO of a subclass.
 
I

Ian Kelly

When I said "super().__init__()" it could have been "super().__init__(size+67)" or whatever arguments are needed for __init__

But if you change the base class, couldn't those arguments change?
Then you would have to change the call whether super is used or not.
I believe this is what Steven is getting at.
 
I

Ian Kelly

[1] You *should* call super, unless you have an excellent reason not to,
so that your class doesn't break multiple-inheritance. But you need to do
so with care making sure that the argument signatures are designed for
cooperative use of super.

I disagree. Most classes will not ever be used for multiple
inheritance, and the changes involved in "making sure that the
argument signatures are designed for cooperative use of super" are not
trivial. For one, it means not being able to use positional arguments
in __init__ methods. For two, the
receive-and-strip-off-keyword-arguments approach falls apart if you
have unrelated classes that take the same arguments. For
illustration, suppose you have the following two classes, both of
which use a required Frobnik object to perform their functions.


class A:
def __init__(self, frobnik, **kwargs):
super().__init__(**kwargs)
self._frobnik = frobnik
...

class B:
def __init__(self, frobnik, **kwargs):
super().__init__(**kwargs)
self._frobnik = frobnik
...

Even though these classes have been designed to be cooperative, they
cannot be inherited together. Whichever class is first in the MRO
will receive the frobnik argument and strip it off, and then the other
class's __init__ method will complain of a missing required argument.

There are solutions to this. For instance, you could make frobnik
optional in each class, each one relying on the other to receive the
frobnik argument if it is missing, but this complicates the
implementations and makes it difficult to detect in a timely manner if
the frobnik argument has actually not been supplied. Or you could
change the name of the argument in one of the classes, but then your
library users will complain of inconsistent naming.

What it boils down to is that classes that are expected to be used for
multiple inheritance should be designed to use super cooperatively,
but the majority of classes that you write should not have to deal
with these sorts of restrictions. Classes that will only ever be
singly inherited should be written normally and using whatever
superclass call style feels most appropriate.
 
S

Steven D'Aprano

The MRO of C will not change, but the class that follows C may be
different in the MRO of a subclass.

To quote a famous line from the movie Cool Hand Luke, "what we have here,
is a failure to communicate."

What do you mean by one class following another? Which class is it that
follows C? What subclass are you talking about, and what is it
subclassing?

I have absolutely no idea what you are trying to get across here, why you
think it is important, or whether it matches what Piet is trying to say.
 
M

Manuel Pégourié-Gonnard

Steven D'Aprano scripsit :
To quote a famous line from the movie Cool Hand Luke, "what we have here,
is a failure to communicate."

What do you mean by one class following another? Which class is it
that follows C? What subclass are you talking about, and what is it
subclassing?
I think Piet's (and Ian's) point is, you can't assume that
super().__init__, written in a method of C, is always going to refer to
__init__ of the parent class of C, when a subclass of C is instanciated.

For example:

class C:
def __init__(self):
print("C init, calling C's parent init?")
super().__init__() # sic

class NotParentOfC:
def __init__(self):
print("NotParentOfC init")

class Sub(C, NotParentOfC):
pass

spam = Sub()

When Sub is instantiated, the line marked "sic" calls
NotParentOfC.__init, not object.__init__ (as if would if C was
instantiated).
 

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,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top