Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #30484 > unrolled thread

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

Started byRamchandra Apte <maniandram01@gmail.com>
First post2012-09-29 06:27 -0700
Last post2012-09-30 00:10 -0600
Articles 18 — 7 participants

Back to article view | Back to comp.lang.python


Contents

  Should one always add super().__init__() to the __init__? Ramchandra Apte <maniandram01@gmail.com> - 2012-09-29 06:27 -0700
    Re: Should one always add super().__init__() to the __init__? Ramchandra Apte <maniandram01@gmail.com> - 2012-09-29 06:28 -0700
    Re: Should one always add super().__init__() to the __init__? Ian Kelly <ian.g.kelly@gmail.com> - 2012-09-29 10:59 -0600
    Re: Should one always add super().__init__() to the __init__? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-09-29 17:17 +0000
      Re: Should one always add super().__init__() to the __init__? Devin Jeanpierre <jeanpierreda@gmail.com> - 2012-09-29 14:18 -0400
      Re: Should one always add super().__init__() to the __init__? Chris Angelico <rosuav@gmail.com> - 2012-09-30 04:31 +1000
        Re: Should one always add super().__init__() to the __init__? Piet van Oostrum <piet@vanoostrum.org> - 2012-09-29 17:51 -0400
          Re: Should one always add super().__init__() to the __init__? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-09-30 04:40 +0000
            Re: Should one always add super().__init__() to the __init__? Ian Kelly <ian.g.kelly@gmail.com> - 2012-09-30 00:08 -0600
              Re: Should one always add super().__init__() to the __init__? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-09-30 09:34 +0000
                Re: Should one always add super().__init__() to the __init__? Manuel Pégourié-Gonnard <mpg@elzevir.fr> - 2012-09-30 14:04 +0200
        Re: Should one always add super().__init__() to the __init__? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-09-30 04:37 +0000
          Re: Should one always add super().__init__() to the __init__? Chris Angelico <rosuav@gmail.com> - 2012-09-30 14:53 +1000
          Re: Should one always add super().__init__() to the __init__? Ian Kelly <ian.g.kelly@gmail.com> - 2012-09-30 01:13 -0600
      Re: Should one always add super().__init__() to the __init__? Ramchandra Apte <maniandram01@gmail.com> - 2012-09-29 20:14 -0700
        Re: Should one always add super().__init__() to the __init__? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-09-30 04:23 +0000
          Re: Should one always add super().__init__() to the __init__? Ramchandra Apte <maniandram01@gmail.com> - 2012-09-29 21:55 -0700
            Re: Should one always add super().__init__() to the __init__? Ian Kelly <ian.g.kelly@gmail.com> - 2012-09-30 00:10 -0600

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

FromRamchandra Apte <maniandram01@gmail.com>
Date2012-09-29 06:27 -0700
SubjectShould one always add super().__init__() to the __init__?
Message-ID<d8fc9d99-8dc0-4290-88a0-33cd38d09f7d@googlegroups.com>
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__).

[toc] | [next] | [standalone]


#30486

FromRamchandra Apte <maniandram01@gmail.com>
Date2012-09-29 06:28 -0700
Message-ID<403a13e6-a118-4620-bf3d-827644987d81@googlegroups.com>
In reply to#30484
On Saturday, 29 September 2012 18:57:48 UTC+5:30, Ramchandra Apte  wrote:
> 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.

[toc] | [prev] | [next] | [standalone]


#30508

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-09-29 10:59 -0600
Message-ID<mailman.1640.1348937989.27098.python-list@python.org>
In reply to#30484
On Sat, Sep 29, 2012 at 7:27 AM, Ramchandra Apte <maniandram01@gmail.com> wrote:
> 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.

[toc] | [prev] | [next] | [standalone]


#30514

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-09-29 17:17 +0000
Message-ID<50672d20$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#30484
On Sat, 29 Sep 2012 06:27:47 -0700, Ramchandra Apte wrote:

> 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



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#30520

FromDevin Jeanpierre <jeanpierreda@gmail.com>
Date2012-09-29 14:18 -0400
Message-ID<mailman.1649.1348942728.27098.python-list@python.org>
In reply to#30514
On Sat, Sep 29, 2012 at 1:17 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> 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

[toc] | [prev] | [next] | [standalone]


#30521

FromChris Angelico <rosuav@gmail.com>
Date2012-09-30 04:31 +1000
Message-ID<mailman.1650.1348943511.27098.python-list@python.org>
In reply to#30514
On Sun, Sep 30, 2012 at 3:17 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> 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

[toc] | [prev] | [next] | [standalone]


#30530

FromPiet van Oostrum <piet@vanoostrum.org>
Date2012-09-29 17:51 -0400
Message-ID<m23920jy5a.fsf@cochabamba.vanoostrum.org>
In reply to#30521
Chris Angelico <rosuav@gmail.com> writes:

> On Sun, Sep 30, 2012 at 3:17 AM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> 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.

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.
-- 
Piet van Oostrum <piet@vanoostrum.org>
WWW: http://pietvanoostrum.com/
PGP key: [8DAE142BE17999C4]

[toc] | [prev] | [next] | [standalone]


#30539

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-09-30 04:40 +0000
Message-ID<5067cd36$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#30530
On Sat, 29 Sep 2012 17:51:29 -0400, Piet van Oostrum wrote:

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



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#30543

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-09-30 00:08 -0600
Message-ID<mailman.1663.1348985321.27098.python-list@python.org>
In reply to#30539
On Sat, Sep 29, 2012 at 10:40 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> On Sat, 29 Sep 2012 17:51:29 -0400, Piet van Oostrum wrote:
>
>> 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.

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

[toc] | [prev] | [next] | [standalone]


#30547

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-09-30 09:34 +0000
Message-ID<50681219$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#30543
On Sun, 30 Sep 2012 00:08:03 -0600, Ian Kelly wrote:

> On Sat, Sep 29, 2012 at 10:40 PM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> On Sat, 29 Sep 2012 17:51:29 -0400, Piet van Oostrum wrote:
>>
>>> 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.
> 
> 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.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#30548

FromManuel Pégourié-Gonnard <mpg@elzevir.fr>
Date2012-09-30 14:04 +0200
Message-ID<k49cfk$hqo$1@thue.elzevir.fr>
In reply to#30547
Steven D'Aprano scripsit :

> On Sun, 30 Sep 2012 00:08:03 -0600, Ian Kelly wrote:
>
>> On Sat, Sep 29, 2012 at 10:40 PM, Steven D'Aprano
>> <steve+comp.lang.python@pearwood.info> wrote:
>>> On Sat, 29 Sep 2012 17:51:29 -0400, Piet van Oostrum wrote:
>>>
>>>> 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.
>> 
>> 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 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).

-- 
Manuel Pégourié-Gonnard - http://people.math.jussieu.fr/~mpg/


[toc] | [prev] | [next] | [standalone]


#30538

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-09-30 04:37 +0000
Message-ID<5067cc81$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#30521
On Sun, 30 Sep 2012 04:31:48 +1000, Chris Angelico wrote:

> On Sun, Sep 30, 2012 at 3:17 AM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> 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.

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.

-- 
Steven

[toc] | [prev] | [next] | [standalone]


#30540

FromChris Angelico <rosuav@gmail.com>
Date2012-09-30 14:53 +1000
Message-ID<mailman.1661.1348980792.27098.python-list@python.org>
In reply to#30538
On Sun, Sep 30, 2012 at 2:37 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> 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

[toc] | [prev] | [next] | [standalone]


#30545

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-09-30 01:13 -0600
Message-ID<mailman.1665.1348989229.27098.python-list@python.org>
In reply to#30538
On Sat, Sep 29, 2012 at 10:37 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> [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.

[toc] | [prev] | [next] | [standalone]


#30534

FromRamchandra Apte <maniandram01@gmail.com>
Date2012-09-29 20:14 -0700
Message-ID<46096186-8383-4990-99c9-61c87f1c5a2f@googlegroups.com>
In reply to#30514
On Saturday, 29 September 2012 22:47:20 UTC+5:30, Steven D'Aprano  wrote:
> On Sat, 29 Sep 2012 06:27:47 -0700, Ramchandra Apte wrote:
> 
> 
> 
> > 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
> 
> 
> 
> 
> 
> 
> 
> -- 
> 
> Steven

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

[toc] | [prev] | [next] | [standalone]


#30537

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-09-30 04:23 +0000
Message-ID<5067c951$0$29981$c3e8da3$5496439d@news.astraweb.com>
In reply to#30534
On Sat, 29 Sep 2012 20:14:10 -0700, Ramchandra Apte wrote:

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



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#30541

FromRamchandra Apte <maniandram01@gmail.com>
Date2012-09-29 21:55 -0700
Message-ID<66f99af0-39c2-4ef1-8f1e-dac17924432f@googlegroups.com>
In reply to#30537
On Sunday, 30 September 2012 09:53:45 UTC+5:30, Steven D'Aprano  wrote:
> On Sat, 29 Sep 2012 20:14:10 -0700, Ramchandra Apte wrote:
> 
> 
> 
> > 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.
> 
> 
> 
> 
> 
> 
> 
> -- 
> 
> Steven

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

[toc] | [prev] | [next] | [standalone]


#30544

FromIan Kelly <ian.g.kelly@gmail.com>
Date2012-09-30 00:10 -0600
Message-ID<mailman.1664.1348985440.27098.python-list@python.org>
In reply to#30541
On Sat, Sep 29, 2012 at 10:55 PM, Ramchandra Apte
<maniandram01@gmail.com> wrote:
> 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.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web