Calling __init__ with multiple inheritance

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Axel Straschil

    #1

    Calling __init__ with multiple inheritance

    Hello!

    Im working with new (object) classes and normaly call init of ther
    motherclass with callin super(...), workes fine.

    No, I've got a case with multiple inherance and want to ask if this is
    the right and common case to call init:

    class Mother(object):
    def __init__(self, param_mother): print 'Mother'
    class Father(object):
    def __init__(self, param_father): print 'Father'
    class Child(Mother, Father):
    def __init__(self, param_mother, param_father):
    Mother.__init__ (self, param_mother)
    Father.__init__ (self, param_mother)
    child = Child(1, 2)

    Thanks, AXEL.
  • Peter Otten

    #2
    Re: Calling __init__ with multiple inheritance

    Axel Straschil wrote:
    [color=blue]
    > Im working with new (object) classes and normaly call init of ther
    > motherclass with callin super(...), workes fine.
    >
    > No, I've got a case with multiple inherance and want to ask if this is
    > the right and common case to call init:
    >
    > class Mother(object):
    > def __init__(self, param_mother): print 'Mother'
    > class Father(object):
    > def __init__(self, param_father): print 'Father'
    > class Child(Mother, Father):
    > def __init__(self, param_mother, param_father):
    > Mother.__init__ (self, param_mother)
    > Father.__init__ (self, param_mother)
    > child = Child(1, 2)[/color]

    It looks correct -- at least it /looked/ correct before tried to quote it...

    Here is an alternative approach that massages the initializer signatures a
    bit to work with super() in a multiple-inheritance environment:

    class Mother(object):
    def __init__(self, p_mother, **more):
    print "Mother", p_mother, more
    super(Mother, self).__init__( p_mother=p_moth er, **more)

    class Father(object):
    def __init__(self, p_father, **more):
    print "Father", p_father, more
    super(Father, self).__init__( p_father=p_fath er, **more)

    class Child(Mother, Father):
    def __init__(self, p_mother, p_father, **more):
    print "Child", p_father, p_mother, more
    super(Child, self).__init__( p_mother=p_moth er, p_father=p_fath er,
    **more)

    Child(1, 2)

    Unrecognized parameters are stashed away into the <more> dictionary.

    Peter

    Comment

    • phil_nospam_schmidt@yahoo.com

      #3
      Re: Calling __init__ with multiple inheritance

      What if I want to call other methods as well? Modifying your example a
      bit, I'd like the reset() method call of Child to invoke both the
      Mother and Father reset() methods, without referencing them by name,
      i.e., Mother.reset(se lf).

      -------------------
      class Mother(object):
      def __init__(self, p_mother, **more):
      print "Mother", p_mother, more
      super(Mother, self).__init__( p_mother=p_moth er, **more)
      def reset(self):
      print 'resetting Mother'

      class Father(object):
      def __init__(self, p_father, **more):
      print "Father", p_father, more
      super(Father, self).__init__( p_father=p_fath er, **more)
      def reset(self):
      print 'resetting Father'

      class Child(Mother, Father):
      def __init__(self, p_mother, p_father, **more):
      print "Child", p_mother, p_father, more
      super(Child, self).__init__( p_mother=p_moth er,
      p_father=p_fath er, **more)
      def reset(self):
      print 'resetting Child'

      # I would like to invoke both Mother.reset()
      # and Father.reset() here, but it doesn't work.
      print 'trying "super"'
      super(Child, self).reset()

      # These next two do work, but require referencing
      # Mother and Father directly.
      print "calling directly"
      Mother.reset(se lf)
      Father.reset(se lf)

      a = Child(1, 2)
      a.reset()
      -------------------

      Thanks,
      Phil

      Comment

      • Peter Otten

        #4
        Cooperative methods, was Re: Calling __init__ with multiple inheritance

        phil_nospam_sch midt@yahoo.com wrote:
        [color=blue]
        > What if I want to call other methods as well? Modifying your example a
        > bit, I'd like the reset() method call of Child to invoke both the
        > Mother and Father reset() methods, without referencing them by name,
        > i.e., Mother.reset(se lf).[/color]

        [example snipped]

        The problem with that aren't incompatible signatures, but the lack of an
        implementation of the reset() method at the top of the diamond-shaped
        inheritance graph that does _not_ call the superclass method. That method
        could be basically a noop:


        class Base(object):
        def reset(self):
        # object does something similar for __init__()
        pass

        class Mother(Base):
        def reset(self):
        print "resetting Mother"
        super(Mother, self).reset()

        class Father(Base):
        def reset(self):
        print "resetting Father"
        super(Father, self).reset()

        class Child(Mother, Father):
        def reset(self):
        print "resetting Child"
        super(Child, self).reset()


        Child().reset()

        It might sometimes be convenient if such methods would magically spring into
        existence for the object class, but for now you have to do it manually (or
        perhaps someone has come up with a metaclass that I am not aware of).

        Peter

        PS: See http://www.python.org/2.2/descrintro.html for more on the subject.

        Comment

        • phil_nospam_schmidt@yahoo.com

          #5
          Re: Cooperative methods, was Re: Calling __init__ with multiple inheritance

          Peter Otten wrote:

          <snip>[color=blue]
          > The problem with that aren't incompatible signatures, but the lack of[/color]
          an[color=blue]
          > implementation of the reset() method at the top of the diamond-shaped
          > inheritance graph that does _not_ call the superclass method. That[/color]
          method[color=blue]
          > could be basically a noop:[/color]

          <snip>

          Ok, now that's cool! Up until now I've been getting along just fine
          without new-style classes, but that is about to change. Thanks for the
          tip!

          Phil

          Comment

          • jfj

            #6
            Re: Calling __init__ with multiple inheritance

            Peter Otten wrote:

            [color=blue]
            > Here is an alternative approach that massages the initializer signatures a
            > bit to work with super() in a multiple-inheritance environment:[/color]
            [color=blue]
            > super(Father, self).__init__( p_father=p_fath er, **more)[/color]


            Is there any advantage using super in this case?
            I think the case Father.__init__ (self, params) is simpler
            and does the job perfectly well.

            super seems to be needed in "Dynamic Inheritance" cases where
            we don't know an object's bases and there are comlicated mro issues!


            jfj

            Comment

            • Peter Otten

              #7
              Re: Calling __init__ with multiple inheritance

              jfj wrote:
              [color=blue]
              > Peter Otten wrote:[/color]
              [color=blue][color=green]
              >> Here is an alternative approach that massages the initializer signatures
              >> a bit to work with super() in a multiple-inheritance environment:[/color]
              >[color=green]
              >> super(Father, self).__init__( p_father=p_fath er, **more)[/color]
              >
              >
              > Is there any advantage using super in this case?
              > I think the case Father.__init__ (self, params) is simpler
              > and does the job perfectly well.[/color]

              I agree.
              [color=blue]
              > super seems to be needed in "Dynamic Inheritance" cases where
              > we don't know an object's bases and there are comlicated mro issues![/color]

              Suppose you wanted factor out common code from the Father and Mother classes
              into a Parent class -- something neither complicated nor farfetched. With
              explicit calls to Parent.__init__ () you would end up calling it twice from
              Child.__init__( ). So when you anticipate that your class hierarchy may
              change, or that your classes may be subclassed by users of your library, I
              think super() is somewhat less errorprone.

              Peter

              Comment

              • Michael Spencer

                #8
                Re: Calling __init__ with multiple inheritance

                phil_nospam_sch midt@yahoo.com wrote:[color=blue]
                > What if I want to call other methods as well? Modifying your example a
                > bit, I'd like the reset() method call of Child to invoke both the
                > Mother and Father reset() methods, without referencing them by name,
                > i.e., Mother.reset(se lf).
                >
                > -------------------
                > class Mother(object):
                > def __init__(self, p_mother, **more):
                > print "Mother", p_mother, more
                > super(Mother, self).__init__( p_mother=p_moth er, **more)
                > def reset(self):
                > print 'resetting Mother'
                >
                > class Father(object):
                > def __init__(self, p_father, **more):
                > print "Father", p_father, more
                > super(Father, self).__init__( p_father=p_fath er, **more)
                > def reset(self):
                > print 'resetting Father'
                >
                > class Child(Mother, Father):
                > def __init__(self, p_mother, p_father, **more):
                > print "Child", p_mother, p_father, more
                > super(Child, self).__init__( p_mother=p_moth er,
                > p_father=p_fath er, **more)
                > def reset(self):
                > print 'resetting Child'
                >
                > # I would like to invoke both Mother.reset()
                > # and Father.reset() here, but it doesn't work.
                > print 'trying "super"'
                > super(Child, self).reset()
                >
                > # These next two do work, but require referencing
                > # Mother and Father directly.
                > print "calling directly"
                > Mother.reset(se lf)
                > Father.reset(se lf)
                >
                > a = Child(1, 2)
                > a.reset()
                > -------------------
                >
                > Thanks,
                > Phil
                >[/color]
                if you're going to be doing a bunch of these, you could define your own variant
                of the super type that calls all the methods in the mro with the given name.
                Below is a minor modification to the pure-python version of built-in super that
                illustrates this. There is no error-checking (e.g., if an attribute is callable
                in one class, and not callable in another you will hit problems) and it's not
                tested beyond what you see:

                """Hack on Super defined in http://python.org/2.2/descrintro.html
                to call all super-class methods"""

                class PolySuper(objec t):
                def __init__(self, type, obj=None):
                self.__type__ = type
                self.__obj__ = obj
                def __get__(self, obj, type=None):
                if self.__obj__ is None and obj is not None:
                return Super(self.__ty pe__, obj)
                else:
                return self
                def __getattr__(sel f, attr):
                if isinstance(self .__obj__, self.__type__):
                starttype = self.__obj__.__ class__
                else:
                starttype = self.__obj__
                mro = iter(starttype. __mro__)
                for cls in mro:
                if cls is self.__type__:
                break
                # Note: mro is an iterator, so the second loop
                # picks up where the first one left off!
                for cls in mro:
                if attr in cls.__dict__:
                x = cls.__dict__[attr]
                if hasattr(x, "__get__"):
                x = x.__get__(self. __obj__)

                # return x
                # We want all the resolved methods, in order
                yield x
                #raise AttributeError, attr

                # Add this crude callable interface
                def __call__(self, attr, *args, **kw):
                methods = list(self.__get attr__(attr))
                return [method(*args, **kw) for method in methods]


                Then, your classes get defined like so:

                class Mother(object):
                def __init__(self, p_mother, **more):
                print "Mother", p_mother, more
                def reset(self):
                print 'resetting Mother'

                class Father(object):
                def __init__(self, p_father, **more):
                print "Father", p_father, more
                def reset(self):
                print 'resetting Father'

                class Child(Mother, Father):
                def __init__(self, p_mother, p_father, **more):
                print "Child", p_mother, p_father, more
                PolySuper(Child , self)("__init__ ", p_mother = p_mother, p_father =
                p_father)
                def reset(self):
                print 'resetting Child'
                PolySuper(Child , self)("reset")
                [color=blue][color=green][color=darkred]
                >>> c = Child("M1","F1" )[/color][/color][/color]
                Child M1 F1 {}
                Mother M1 {'p_father': 'F1'}
                Father F1 {'p_mother': 'M1'}[color=blue][color=green][color=darkred]
                >>> c.reset()[/color][/color][/color]
                resetting Child
                resetting Mother
                resetting Father[color=blue][color=green][color=darkred]
                >>>[/color][/color][/color]

                HTH
                Michael

                Comment

                • jfj

                  #9
                  Re: Calling __init__ with multiple inheritance

                  Peter Otten wrote:
                  [color=blue]
                  > jfj wrote:
                  >[color=green]
                  >>Peter Otten wrote:[/color]
                  >[color=green][color=darkred]
                  >>>Here is an alternative approach that massages the initializer signatures
                  >>>a bit to work with super() in a multiple-inheritance environment:[/color]
                  >>[color=darkred]
                  >>> super(Father, self).__init__( p_father=p_fath er, **more)[/color]
                  >>
                  >>Is there any advantage using super in this case?
                  >>I think the case Father.__init__ (self, params) is simpler
                  >>and does the job perfectly well.[/color]
                  >
                  > I agree.
                  >[/color]

                  Ok. thanks for the confirmation.
                  [color=blue]
                  >[color=green]
                  >>super seems to be needed in "Dynamic Inheritance" cases where
                  >>we don't know an object's bases and there are comlicated mro issues![/color]
                  >
                  >
                  > Suppose you wanted factor out common code from the Father and Mother classes
                  > into a Parent class -- something neither complicated nor farfetched. With
                  > explicit calls to Parent.__init__ () you would end up calling it twice from
                  > Child.__init__( ). So when you anticipate that your class hierarchy may
                  > change, or that your classes may be subclassed by users of your library, I
                  > think super() is somewhat less errorprone.
                  >[/color]

                  I accept the case that you avoid bugs if you extend the hierarchy
                  upwards. Although that's rare.

                  As for the case where the users of the library want to subclass, I don't
                  see a problem. They know they must subclass from class XXX and so they
                  call XXX.__init__ to construct it.

                  In the case of Parent diamond inheritance, super() can avoid calling
                  the __init__ of parent twice? How?


                  jfj

                  Comment

                  • Peter Otten

                    #10
                    Re: Calling __init__ with multiple inheritance

                    jfj wrote:
                    [color=blue]
                    > As for the case where the users of the library want to subclass, I don't
                    > see a problem.  They know they must subclass from class XXX and so they
                    > call XXX.__init__ to construct it.[/color]

                    I was thinking of

                    class Child(Father, Mother):
                    pass

                    where Father and Mother have a common base class Parent. That would be
                    initialized twice:
                    [color=blue][color=green][color=darkred]
                    >>> class Parent(object):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "parent"
                    ....[color=blue][color=green][color=darkred]
                    >>> class Father(Parent):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "father"
                    .... Parent.__init__ (self)
                    ....[color=blue][color=green][color=darkred]
                    >>> class Mother(Parent):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "mother"
                    .... Parent.__init__ (self)
                    ....[color=blue][color=green][color=darkred]
                    >>> class Child(Father, Mother):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "child"
                    .... Father.__init__ (self)
                    .... Mother.__init__ (self)
                    ....[color=blue][color=green][color=darkred]
                    >>> Father()[/color][/color][/color]
                    father
                    parent
                    <__main__.Fathe r object at 0x402ad66c>[color=blue][color=green][color=darkred]
                    >>> Mother()[/color][/color][/color]
                    mother
                    parent
                    <__main__.Mothe r object at 0x402ad38c>[color=blue][color=green][color=darkred]
                    >>> Child()[/color][/color][/color]
                    child
                    father
                    parent
                    mother
                    parent # <-- the culprit: parent initialized a second time
                    <__main__.Chi ld object at 0x402ad66c>

                    You have several options now:
                    - Write __init__() in such a way that calling it twice does no harm
                    - Ensure that it is only called once by adding a self.initialize d flag
                    - Document that a user class may not inherit from both Father and Mother
                    but the best is IMO
                    - let super() do the work
                    [color=blue]
                    > In the case of Parent diamond inheritance, super() can avoid calling
                    > the __init__ of parent twice?  How?[/color]

                    That's the best part -- it's automatic:
                    [color=blue][color=green][color=darkred]
                    >>> class Parent(object):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "parent"
                    .... super(Parent, self).__init__( )
                    ....[color=blue][color=green][color=darkred]
                    >>> class Father(Parent):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "father"
                    .... super(Father, self).__init__( )
                    ....[color=blue][color=green][color=darkred]
                    >>> class Mother(Parent):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "mother"
                    .... super(Mother, self).__init__( )
                    ....[color=blue][color=green][color=darkred]
                    >>> class Child(Father, Mother):[/color][/color][/color]
                    .... def __init__(self):
                    .... print "child"
                    .... super(Child, self).__init__( )
                    ....[color=blue][color=green][color=darkred]
                    >>> Father()[/color][/color][/color]
                    father
                    parent
                    <__main__.Fathe r object at 0x402ad38c>[color=blue][color=green][color=darkred]
                    >>> Mother()[/color][/color][/color]
                    mother
                    parent
                    <__main__.Mothe r object at 0x402ad3cc>[color=blue][color=green][color=darkred]
                    >>> Child()[/color][/color][/color]
                    child
                    father
                    mother
                    parent # <-- parent only once
                    <__main__.Chi ld object at 0x402ad38c>

                    That was a lot of dull code -- but you asked :-)
                    Seriously, I think super() scales better in a scenario with multiple
                    inheritance, though I won't start a holy war about the issue.

                    Peter


                    Comment

                    • Axel Straschil

                      #11
                      Re: Calling __init__ with multiple inheritance

                      Hello!

                      Thanks to all for the very interesting postings!

                      I came to the following:

                      For single inheritance, super is a nice tool if you will recfactoring
                      the class later.

                      For multiple inheritance, if you want to use super, you have to have
                      very much knowledge of the classes you inheritance. For me, OOP is to
                      not have to have the deep inner knowledge of the classes I inheritance
                      from.

                      Also, for multiple inheritance, if think
                      Mother1.__init_ _(self, ...)
                      Mother2.__init_ _(self, ...)
                      Mother3.__init_ _(self, ...)
                      would be more clear to read then
                      super(MyClass). __init__(Maby this will do some magic thing)

                      Also, I realy dislike
                      __init__(self, param, **eat_some_need less_stuff)

                      If I later extend my class and also add some parameters to __init__,
                      what will happen to the classes using the baseclass?

                      Also, lool at that:
                      class Mother(object):
                      def __init__(self, param_mother='o ptional', **eat):
                      print 'Mother'
                      class Father(object):
                      def __init__(self, param_father='o ptional', **eat):
                      print 'Father'
                      class Child(Mother, Father):
                      def __init__(self, **ham):
                      super(Child, self).__init__( **ham)
                      child = Child(param_mot her=1, param_father=1)

                      Father's init will not be called.

                      Thanks,
                      AXEL.
                      --
                      "Aber naja, ich bin eher der Forentyp." Wolfibolfi's outing in

                      Comment

                      • Steven Bethard

                        #12
                        Re: Calling __init__ with multiple inheritance

                        Axel Straschil wrote:[color=blue]
                        > I came to the following:
                        >
                        > For single inheritance, super is a nice tool if you will recfactoring
                        > the class later.
                        >
                        > For multiple inheritance, if you want to use super, you have to have
                        > very much knowledge of the classes you inheritance.[/color]

                        And for multiple inheritance, if you don't use super, you're likely to
                        call the same method twice if you ever have a diamond inheritance problem.

                        STeVe

                        Comment

                        • Peter Otten

                          #13
                          Re: Calling __init__ with multiple inheritance

                          Axel Straschil wrote:
                          [color=blue]
                          > Thanks to all for the very interesting postings![/color]

                          You're welcome.
                          [color=blue]
                          > I came to the following:
                          >
                          > For single inheritance, super is a nice tool if you will recfactoring
                          > the class later.[/color]

                          Or if you start out with a diamond inheritance graph from the beginning.
                          [color=blue]
                          > For multiple inheritance, if you want to use super, you have to have
                          > very much knowledge of the classes you inheritance. For me, OOP is to
                          > not have to have the deep inner knowledge of the classes I inheritance
                          > from.[/color]

                          I failed to bring my point that you need _less_ knowledge across then.
                          [color=blue]
                          > Also, for multiple inheritance, if think
                          > Mother1.__init_ _(self, ...)
                          > Mother2.__init_ _(self, ...)
                          > Mother3.__init_ _(self, ...)
                          > would be more clear to read then
                          > super(MyClass). __init__(Maby this will do some magic thing)[/color]

                          For an advantage to show up you need at least three levels of inheritance,
                          see my example with the Parent class.
                          [color=blue]
                          > Also, I realy dislike
                          > __init__(self, param, **eat_some_need less_stuff)[/color]

                          In turn, I really like the name you gave the dictionary parameter :-)
                          Normalize the initializer signature to a common set of arguments. Or pass a
                          single parameter and take the actual information from its attributes. Or
                          write a custom dispatcher, that passes only parameters that correspond to
                          formal arguments...
                          [color=blue]
                          > If I later extend my class and also add some parameters to __init__,
                          > what will happen to the classes using the baseclass?
                          >
                          > Also, lool at that:
                          > class Mother(object):
                          > def __init__(self, param_mother='o ptional', **eat):
                          > print 'Mother'
                          > class Father(object):
                          > def __init__(self, param_father='o ptional', **eat):
                          > print 'Father'
                          > class Child(Mother, Father):
                          > def __init__(self, **ham):
                          > super(Child, self).__init__( **ham)
                          > child = Child(param_mot her=1, param_father=1)
                          >
                          > Father's init will not be called.[/color]

                          Change Father/Mother.__init__ () to call the superclass initializer. It may
                          be counterintuitiv e, but it works.

                          Peter

                          Comment

                          • jfj

                            #14
                            Re: Calling __init__ with multiple inheritance

                            Peter Otten wrote:[color=blue]
                            >[color=green][color=darkred]
                            >>>>Child()[/color][/color]
                            >
                            > child
                            > father
                            > mother
                            > parent # <-- parent only once
                            > <__main__.Chi ld object at 0x402ad38c>
                            >[/color]

                            D-uh?

                            ############### ############### ############### ###
                            class Parent(object):
                            def __init__(self):
                            print "parent"
                            super(Parent, self).__init__( )

                            class Father(Parent):
                            def __init__(self):
                            print "father"
                            super(Father, self).__init__( )
                            print "D-uh"

                            class Mother(Parent):
                            def __init__(self):
                            print "mother"
                            super(Mother, self).__init__( )
                            print "D-oh"

                            class Child(Father, Mother):
                            def __init__(self):
                            print "child"
                            super(Child, self).__init__( )

                            Child()
                            ############### ############### ############### ###

                            This prints
                            child
                            father
                            mother
                            parent
                            D-oh
                            D-uh

                            Therefore super is a very intelligent function indeed!


                            jf

                            Comment

                            • Axel Straschil

                              #15
                              Re: Calling __init__ with multiple inheritance

                              Hello!
                              [color=blue][color=green]
                              >> Also, lool at that:
                              >> class Mother(object):
                              >> def __init__(self, param_mother='o ptional', **eat):
                              >> print 'Mother'
                              >> class Father(object):
                              >> def __init__(self, param_father='o ptional', **eat):
                              >> print 'Father'
                              >> class Child(Mother, Father):
                              >> def __init__(self, **ham):
                              >> super(Child, self).__init__( **ham)
                              >> child = Child(param_mot her=1, param_father=1)
                              >> Father's init will not be called.[/color]
                              > Change Father/Mother.__init__ () to call the superclass initializer. It may
                              > be counterintuitiv e, but it works.[/color]

                              OK, thanks, with the super(...).__in it__() in Father/Mother it workes
                              and makes sense.

                              So, the last thing a *realy* don't like ist the
                              __init__(self, param, **ignore_the_re st) thing.

                              Anyone had troubles with that, or should I cust take this as a "python
                              way of thinking" ... ;-), and getting used to that?

                              Thanks,
                              AXEL.
                              --
                              "Aber naja, ich bin eher der Forentyp." Wolfibolfi's outing in

                              Comment

                              Working...