Re: Why Isn't Multiple Inheritance Automatic in Python?

Discussion in 'Python' started by Ian Kelly, Dec 17, 2012.

  1. Ian Kelly

    Ian Kelly Guest

    On Sun, Dec 16, 2012 at 9:30 PM, Nick M. Daly <> wrote:
    > It's very unlikely that multiple inheritance would go horribly wrong, as
    > long as classes adopt class-specific argument naming conventions.
    > However, ever since bug 1683368 [0] was fixed, it's now impossible to
    > cleanly create arbitrary inheritance trees.


    No, it isn't. You just code each class to strip out the particular
    arguments that it uses, and by the time you get up to object, either
    you've removed all the arguments, or your inheritance tree is buggy.
    The fix for bug 1683368 means that this latter case is detected and
    raised as an error.

    There was a thread not too long ago about the fact that this fix was
    recently extended to the __init__ methods of immutable classes, and
    while I'm not convinced that this was the correct thing to do, Terry
    Reedy pointed out in the issue comments back in 2010 that the proper
    way to initialize immutable instances is by overriding __new__ rather
    than __init__, the former of which is still perfectly clean to
    inherit.

    > The only reason I can't
    > just take anybody's code and plop it into my inheritance tree is because
    > Python demands that each class specifically opts in to MI though
    > mechanisms like the following:
    >
    > 1: class Foo(object):
    > 2: def __init__(self, foo_bar, *args, **kwargs):
    > 3: if Foo.__mro__[1] != object:
    > 4: super().__init__(*args, **kwargs)
    > 5:
    > 6: self.bar = foo_bar
    >
    > Lines 3 and 4 are required because Foo might fall at the beginning or
    > the middle of in the inheritance tree: we can't know ahead of time.


    Of course we can know the full MRO of Foo just by looking at this
    code. Foo derives from object, and nothing else. The MRO of Foo is
    therefore (Foo, object), and the test is always false. What we can't
    know ahead of time is the MRO of *self*, which could be an instance of
    a subclass of Foo. But line 3 is not testing the MRO of self, only of
    Foo. If self happens to be an instance of FooBar, with the MRO
    (FooBar, Foo, Bar, object), then the above code will cause bugs,
    because Bar.__init__ is never called.

    In any case, these lines are not necessary. The only reason not to
    call super() in cooperative MI is if the method does not exist on the
    super object. A better way to test this would be:

    if hasattr(super(), '__init__'):
    super().__init__(**kwargs)

    However, that test is still silly, since __init__ is a method of
    object and *always* exists. For non-init methods, best practice is to
    use a root class (as recommended by Raymond Hettinger [1]). Anything
    that implements the method would inherit from the root class (to
    ensure that it will precede the root class in the MRO) and call
    super(). The root class serves only to end the chain and does not
    call super().


    > From my perspective, it'd be lovely if init methods implicitly accepted
    > *args and **kwargs while implicitly sending them off to the next class
    > in the MRO as the first call. This would make the previous example
    > semantically equivalent to:
    >
    > 1: class Foo(object):
    > 2: def __init__(self, foo_bar):
    > 3: self.bar = foo_bar
    >
    > Granted, that's probably too excessive and implicit for most folks to be
    > comfortable with, even though that's obviously the behavior a user
    > intends when they write code like:
    >
    > 1: class Baz(Foo, Bar):
    > 2: def __init__(self):
    > 3: super().__init__(foo_bar=1, bar_quote="Give me another!")


    I don't find that obvious at all. Does the implicit super() call
    happen before or after the body of the method? There are cases where
    the subclass may want to have some code before and some code after.
    How do you implicitly call super() in methods that return a value --
    what do you implicitly do with the return value of the super() call?
    How do you write methods that intentionally do not call super, such as
    in the root classes mentioned above, or in methods that are simply
    meant to be overridden and not extended? If the user calls
    Baz(foo_bar=42), then does the super() call from Baz still pass
    foo_bar=1, or does it implicitly call super() with foo_bar=42 instead,
    or does it try to do "super().__init__(foo_bar=1, bar_quote="Give me
    another!", foo_bar=42)" and raise a TypeError due to repeated
    arguments?
     
    Ian Kelly, Dec 17, 2012
    #1
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Vla
    Replies:
    30
    Views:
    1,029
    Jerry Coffin
    Jan 25, 2005
  2. Mr. SweatyFinger

    why why why why why

    Mr. SweatyFinger, Nov 28, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    913
    Mark Rae
    Dec 21, 2006
  3. Mr. SweatyFinger
    Replies:
    2
    Views:
    2,008
    Smokey Grindel
    Dec 2, 2006
  4. Nick M. Daly
    Replies:
    0
    Views:
    138
    Nick M. Daly
    Dec 17, 2012
  5. Terry Reedy
    Replies:
    0
    Views:
    103
    Terry Reedy
    Dec 17, 2012
Loading...

Share This Page