super(...).__init__() vs Base.__init__(self)

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Kent Johnson

    #1

    super(...).__init__() vs Base.__init__(self)

    Are there any best practice guidelines for when to use
    super(Class, self).__init__( )
    vs
    Base.__init__(s elf)
    to call a base class __init__()?

    The super() method only works correctly in multiple inheritance when the
    base classes are written to expect it, so "Always use super()" seems
    like bad advice. OTOH sometimes you need super() to get correct
    behaviour. ISTM "Only use super() when you know you need it" might be
    the best advice. Is there any conventional wisdom on this?

    The question arises from a naive use of super() in a post on the tutor
    list. This code gives an AttributeError because Base.__init__() is never
    called:

    import threading

    class Base(object):
    def __init__(self):
    self.x = 1

    class Derived(threadi ng.Thread, Base):
    def __init__(self):
    super(Derived, self).__init__( )

    d=Derived()
    d.x

    If the order of base classes is reversed, the reference to d.x works but
    of course threading.Threa d.__init__() is never called.

    1. One way to fix the code is to call Base.__init__() and
    threading.Threa d.__init__() explicitly in Derived.__init_ _().

    2. Another fix is for Base.__init__() to call super(Base,
    self).__init__( ) and to list Base first in the list of base classes.
    This is fragile - it depends on the order of base classes and adding
    another base class would break it.

    3. A third fix might be to change both Base and threading.Threa d() to
    call super(...).__in it__(). This might break existing code that is
    written in the style of fix 1 (calling both base class __init__()
    methods explicitly).

    I prefer the first fix, it is explicit and fairly robust - it works if
    the order of bases is changed, and it's pretty clear from the body of
    Derived.__init_ _() that if you add another base class, you should change
    __init__().

    Any other opinions? Any consensus about the "best" way to do this?

    BTW I understand what super() does, I know why the original code is
    broken, I'm not asking for help with that. I'm wondering what others
    think best practices are.

    Thanks,
    Kent
  • Michele Simionato

    #2
    Re: super(...).__in it__() vs Base.__init__(s elf)

    I remember there was somewhere a page called "super considered
    harmful", some googling
    should find it. It was discussing the issue you are alluding to, as
    well others. Also google
    in the newsgroup, there are lots of threads about super and its
    shortcomings.

    Michele Simionato

    Comment

    • Steven Bethard

      #3
      Re: super(...).__in it__() vs Base.__init__(s elf)

      Kent Johnson wrote:[color=blue]
      > Are there any best practice guidelines for when to use
      > super(Class, self).__init__( )
      > vs
      > Base.__init__(s elf)
      > to call a base class __init__()?
      >[/color]
      [snip][color=blue]
      >
      > 3. A third fix might be to change both Base and threading.Threa d() to
      > call super(...).__in it__(). This might break existing code that is
      > written in the style of fix 1 (calling both base class __init__()
      > methods explicitly).[/color]

      Personally, I'd call the lack of the super calls in threading.Threa d and
      Base bugs. So code relying on that behavior needs to be fixed when the
      bug is fixed. But __init__() is definitely a tricky case since the
      number of arguments tends to change in the __init__() methods of classes...

      STeVe

      Comment

      • Jan Niklas Fingerle

        #4
        Re: super(...).__in it__() vs Base.__init__(s elf)

        Steven Bethard <steven.bethard @gmail.com> wrote:[color=blue]
        > Personally, I'd call the lack of the super calls in threading.Threa d and
        > Base bugs.[/color]

        It can't be a bug since it wasn't a bug before super was introduced and
        you don't wan't to break working Python-2.x-code.
        [color=blue]
        > But __init__() is definitely a tricky case since the
        > number of arguments tends to change in the __init__() methods of classes...[/color]

        ACK. And every __init__ will have to accept *any* arguments you give to
        it and call super with *all* the arguments it got. This is tricky and
        easily to get wrong. Super is a good tool to use, when dealing with
        diamond shape inheritance. In any other case I would use the direct
        calls to the base classes. In fact, i've yet to find a non-textbook-case
        where I really need diamond shape inheritance. OTOH I don't mean to say
        that noone else needs it either.

        cu,
        --Jan Niklas

        Comment

        • Steven Bethard

          #5
          Re: super(...).__in it__() vs Base.__init__(s elf)

          Jan Niklas Fingerle wrote:[color=blue]
          > Steven Bethard <steven.bethard @gmail.com> wrote:[color=green]
          >> Personally, I'd call the lack of the super calls in threading.Threa d and
          >> Base bugs.[/color]
          >
          > It can't be a bug since it wasn't a bug before super was introduced and
          > you don't wan't to break working Python-2.x-code.[/color]

          Just because there wasn't a bugfix available at the time doesn't mean it
          wasn't a bug. ;) The threading.Threa d class does not properly call
          sibling constructors in multiple inheritance. This should either be
          fixed in the implementation (by introducing a call to super) or fixed in
          the documentation (by indicating that threading.Threa d does not support
          multiple inheritance in its __init__() method).
          [color=blue][color=green]
          >> But __init__() is definitely a tricky case since the
          >> number of arguments tends to change in the __init__() methods of classes...[/color]
          >
          > ACK. And every __init__ will have to accept *any* arguments you give to
          > it and call super with *all* the arguments it got. This is tricky and
          > easily to get wrong. Super is a good tool to use, when dealing with
          > diamond shape inheritance. In any other case I would use the direct
          > calls to the base classes. In fact, i've yet to find a non-textbook-case
          > where I really need diamond shape inheritance. OTOH I don't mean to say
          > that noone else needs it either.[/color]

          I've used diamond inheritance exactly once, and all the classes under
          that hierarchy were under my control, so they all used super properly.
          And fortunately, the constructors of those classes didn't take any
          arguments, so I didn't run into any of the nastier sides of super.

          Using super is guaranteed to work as long as the number of arguments of
          the method does not change from that of the superclass. For the
          __init__() method, this means that super is guaranteed to work as long
          as it takes no arguments, since object.__init__ () takes no arguments.
          Sure, I'd love to see super work right in other cases, but for the OP's
          situation at least, super already does what it's supposed to.

          STeVe

          Comment

          • Tony Nelson

            #6
            Re: super(...).__in it__() vs Base.__init__(s elf)

            In article <43eba7f7$0$507 $9b4e6d93@newsr ead4.arcor-online.net>,
            Jan Niklas Fingerle <usenet-2004@lithe.de> wrote:
            [color=blue]
            > ...Super is a good tool to use, when dealing with
            > diamond shape inheritance. In any other case I would use the direct
            > calls to the base classes. In fact, i've yet to find a non-textbook-case
            > where I really need diamond shape inheritance. ...[/color]

            As long as you don't use multiple inheritance with new-style classes,
            you'll be fine.
            _______________ _______________ _______________ _______________ ____________
            TonyN.:' *firstname*nlsn ews@georgea*las tname*.com
            ' <http://www.georgeanels on.com/>

            Comment

            • Jan Niklas Fingerle

              #7
              Re: super(...).__in it__() vs Base.__init__(s elf)

              Tony Nelson <*firstname*nls news@georgea*la stname*.com> wrote:[color=blue]
              > In article <43eba7f7$0$507 $9b4e6d93@newsr ead4.arcor-online.net>,
              > Jan Niklas Fingerle <usenet-2004@lithe.de> wrote:
              >[color=green]
              > > ...Super is a good tool to use, when dealing with
              > > diamond shape inheritance. In any other case I would use the direct
              > > calls to the base classes. In fact, i've yet to find a non-textbook-case
              > > where I really need diamond shape inheritance. ...[/color]
              >
              > As long as you don't use multiple inheritance with new-style classes,
              > you'll be fine.[/color]

              OK, I should have written: "... diamond shape inheritance where the base
              class's methods have to be called cooperatively ..."

              In other words: In almost every real world example of diamond shape
              inheritance where the base class is "only" object we don't have a
              problem, because you don't have to call object's __init__, yet it causes
              no harm if you call it twice - and __init__ is the *the* method you most
              commonly would use "super()" for.

              So, yes, "no multiple inheritance" is sufficient, but not nessecary to
              live happily without ever using "super()".

              Nothing against super where it's appropiate. But don't optimize for
              ("real") diamond shape inheritance, before you really need it...

              Cheers,
              --Jan Niklas

              Comment

              • Jan Niklas Fingerle

                #8
                Re: super(...).__in it__() vs Base.__init__(s elf)

                Steven Bethard <steven.bethard @gmail.com> wrote:[color=blue]
                > Jan Niklas Fingerle wrote:[color=green]
                > > Steven Bethard <steven.bethard @gmail.com> wrote:[color=darkred]
                > >> Personally, I'd call the lack of the super calls in threading.Threa d and
                > >> Base bugs.[/color]
                > >
                > > It can't be a bug since it wasn't a bug before super was introduced and
                > > you don't wan't to break working Python-2.x-code.[/color]
                >
                > Just because there wasn't a bugfix available at the time doesn't mean it
                > wasn't a bug. ;)[/color]

                Yes, but it isn't a bug.
                [color=blue]
                > The threading.Threa d class does not properly call
                > sibling constructors in multiple inheritance. > This should either be
                > fixed in the implementation (by introducing a call to super)[/color]

                This would break existing code as shown in http://fuhm.org/super-harmful/
                (look out for "Subclasses must use super if their superclasses do").
                And as much as I agree with GvR that the word "harmful" is inappropriate
                (http://mail.python.org/pipermail/pyt...y/050656.html),
                I agree with both, that super is a part of your class's interface that
                you might use or not. You just have to document it, whether you use
                super, or not. Or to quote GvR: "Super is intended for use that are
                designed with method cooperation in mind [...]". That's isn't "always".

                The best pratices (see any of the two URLs above) obviously show, that
                using super comes at some cost. This is OK, if you really have to
                support cooperative method calling. But I wouldn't want to pay it "just
                in case".
                [color=blue]
                > or fixed in
                > the documentation[/color]

                This is true, but this or the other way round: You have to document,
                that you're using super(), or that you don't do it.
                [color=blue]
                > (by indicating that threading.Threa d does not support
                > multiple inheritance in its __init__() method).[/color]

                It *does* support multiple inheritance, it just doesn't support diamond
                shape inheritance (not counting object).
                [color=blue]
                > I've used diamond inheritance exactly once, and all the classes under
                > that hierarchy were under my control, so they all used super properly.[/color]

                And this is, what super() is meant for. I, for my part, won't use
                super() until I really need it, but when the time comes I will
                worship an extra hour at my Guido-van-Rossum-shrine. ;-)

                Comment

                Working...