0

I was watching a Python talk at Youtube and found an interesting language feature. However when I tried to run a test code it didn't work and I'd like to understand why.

I was expecting this to print this:

Parent
OtherParent

But instead I got this:

Parent
Parent

Sample code:

class Parent:
    def get_message(self):
        return "Parent"

class Child(Parent):
    def do_print(self):
        print(super().get_message())

class OtherParent:
    def get_message(self):
        return "OtherParent"

class OtherChild(Child, OtherParent):
    pass

Child().do_print()
OtherChild().do_print()

Edit: Running on Windows, Python 3.5.1, Anaconda 4.0.0 (64-bit)

NikoNyrh
  • 3,578
  • 2
  • 18
  • 32
  • What is your python version? – Ohumeronen Jul 18 '16 at 13:34
  • 2
    `print(OtherChild.__mro__)` should answer your question – jonrsharpe Jul 18 '16 at 13:40
  • 1
    +1 on the `__mro__` explaining the behavior. See [here](http://stackoverflow.com/a/10018792/426790) for an answer about the order of mixins affecting OtherChild (short answer: "The MRO is basically depth-first, left-to-right"). – Greg Sadetsky Jul 18 '16 at 13:43
  • Also, [here](http://blog.codekills.net/2014/04/02/the-sadness-of-pythons-super/)'s an article talking about the possible confusion of using `super()` and mixins at the same time. – Greg Sadetsky Jul 18 '16 at 13:46

2 Answers2

1

The correct explanation is mentioned in the comments of the question, ie from MRO of the OtherChild class (link posted in the comment: How does the order of mixins affect the derived class?).

See the different outputs of the MRO of the OtherChild class depending on the different inheritances:

  1. OtherParent have no parent class:

    class OtherParent():
        def get_message(self):
            return "OtherParent"
    
    print(OtherChild.__mro__)
    Child().do_print()
    OtherChild().do_print()
    

    Output shows that Parent comes before OtherParent:

    (<class '__main__.OtherChild'>, <class '__main__.Child'>, <class '__main__.Parent'>, <class '__main__.OtherParent'>, <class 'object'>)
    Parent
    Parent
    
  2. Parent is the parent class of OtherParent:

    class OtherParent(Parent):
        def get_message(self):
            return "OtherParent"
    

    Output shows that OtherParent comes now before Parent:

    (<class '__main__.OtherChild'>, <class '__main__.Child'>, <class '__main__.OtherParent'>, <class '__main__.Parent'>, <class 'object'>)
    Parent
    OtherParent
    
  3. Still case 2 but now OtherChild inherits first from OtherParent, then Child:

    class OtherParent(Parent):
        def get_message(self):
            return "OtherParent"
    
    class OtherChild(OtherParent, Child):
        pass
    

    Output shows that OtherParent comes again before Parent, but before Child too:

    (<class '__main__.OtherChild'>, <class '__main__.OtherParent'>, <class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>)
    Parent
    Parent
    

Maybe someone can explain the last case which is not natural at first look.

Community
  • 1
  • 1
Frodon
  • 3,684
  • 1
  • 16
  • 33
  • Neither parent has `do_print`, it is only defined in the `Child` class, did you mean `get_message`? But even then I don't quite follow the explanation. – NikoNyrh Jul 18 '16 at 13:58
  • 1
    @NikoNyrh then *why did you accept it?!* Perhaps it would be better to delete this question entirely (you will need to unaccept and unvote this answer), read the linked question on MRO order then ask again if you're still uncertain. As it stands, this whole thing is a car wreck. – jonrsharpe Jul 18 '16 at 14:06
  • Sorry I was too eager to accept a plausible looking answer before reading it in detail :/ I cannot delete the question as it already has answers. Next time I'll do more research before asking the question. – NikoNyrh Jul 18 '16 at 14:21
  • @NikoNyrh I updated my edit to add some exemples with the good explanation (I hope :)) – Frodon Jul 18 '16 at 14:24
-2

Ah my friend pointed out to a small deviation from the example on Youtube:

class Parent:
    def get_message(self):
        return "Parent"

class Child(Parent):
    def do_print(self):
        print(super().get_message())

class OtherParent(Parent): # <----- Inheritance of Parent makes it work
    def get_message(self):
        return "OtherParent"

class OtherChild(Child, OtherParent):
    pass

Child().do_print()
OtherChild().do_print()
NikoNyrh
  • 3,578
  • 2
  • 18
  • 32
  • 3
    This doesn't really explain *why* the first code doesn't work as expected, or how this change fixes it. – jonrsharpe Jul 18 '16 at 13:41
  • Unfortunately I don't know inner workings of Python to know the answer in detail. – NikoNyrh Jul 18 '16 at 13:43
  • 1
    Oh, you're the OP - then you could add this to the question, rather than posting it as an answer: *"it works as expected if I make `OtherParent` inherit from `Parent`, but I don't understand why"* – jonrsharpe Jul 18 '16 at 13:44
  • True, although my question was more on "why doesn't this code work as advertised" and the answer was I didn't implement it properly. In hindsight I might as well have deleted the question. – NikoNyrh Jul 18 '16 at 13:51
  • But if you don't understand the difference, *why* adding that inheritance alters the behaviour, then you may still have a question. – jonrsharpe Jul 18 '16 at 13:52