2

I am starting to learn the Python language and need a critique of my code as well as solution to an error message I am getting.

The code creates a Robot class with two ancestors; Transformer and Gundam. A third class Hybrid inherits from these. I can instantiate a Transformer and a Gundam without error. Instantiating a Hybrid returns an error. Code and error follow below:

Main.py

from multiple_inheritance import Robot, Transformer, Gundam, Hybrid

tito = Gundam("Tito", "Japan")
optimus = Transformer("Optimus Prime", "Mars")
Jaeger = Hybrid("Striker", "US")

print(tito)
print(optimus)

multiple_inheritance

class Robot(object):
    """A robot class"""
    def __init__(self, name):
            self.name = name

    def __str__(self):
            return "{name}".format(name = self.name)

class Transformer(Robot):
    """A transformer"""
    def __init__(self, name, planet):
            self.planet = planet
            super(Transformer, self).__init__(name)

    def __str__(self):
            return "Name = {name}, Planet = {planet}".\
                   format(name = self.name, planet = self.planet)

class Gundam(Robot):
    """A Gundam"""
    def __init__(self, name, country):
            self.country = country
            super(Gundam, self).__init__(name)

    def __str__(self):
            return "Name = {name}, Country = {country}".\
                   format(name = self.name, country = self.country)

class Hybrid(Transformer, Gundam):
    """Ultimate Robot"""

Error Message

Traceback (most recent call last):
  File "D:\Tech\Python\my_code\unit10\multiple_inheritance_main.py", line 5, in <module>
    Jaeger = Hybrid("Striker", "US")
  File "D:\Tech\Python\my_code\unit10\multiple_inheritance.py", line 13, in __init__
    super(Transformer, self).__init__(name)
TypeError: __init__() missing 1 required positional argument: 'country'
1
  • Robot class with two ancestors incorrect. In your code, Robot is the ancestor of both Transformer and Gundam Commented Apr 15, 2014 at 19:27

2 Answers 2

4

Hybrid's mro looks something like this:

>>> Hybrid.__mro__
(<class '__main__.Hybrid'>, <class '__main__.Transformer'>, <class '__main__.Gundam'>, <class '__main__.Robot'>, <type 'object'>)

As you can see after Transformer the next class is Gundam and you're not passing enough arguments to it when calling it from Transformer's __init__:

class Transformer(Robot):
    """A transformer"""
    def __init__(self, name, planet):
            self.planet = planet
            #####This calls Gundam's __init__
            super(Transformer, self).__init__(name)

So, the point is super() calls the next class in MRO not Transformer's base class as you expected.


A very good post related to super(): Python’s super() considered super!

Sign up to request clarification or add additional context in comments.

3 Comments

this makes things a bit complicated. Transformer(Robot) now calls Gundam as super? Then if you create a plain Transformer, how can you tell it which parent to call?
You don't - the MRO (method resolution order) does that for you. For a Plain transformer, the Transformer superclass is "Robot". The class hierarchy you created places Gundam between Transformer and Robot. Otherwise, which of the "super" calls would call's "Robot"'s __init__ - the one in Transformer, or the one in Gundam?
@njzk2 Well super() is bit complicated, Yes for the current MRO super call from Transformer will call Gundam. I really don't understand your second question, the parent can be identified using the class's MRO.
3

As pointed in the other answer, Python builds a MRO (method resolution order) for your class, placing Hybrid->Transformer->Gundam->Robot - that means not only that missing methods and attributes are searched in the classes in this order, but that is the order used when calling methods using the "super" construct.

I don't know of anyone ever saying multiple inheritance was "easy". It was even left altogether out of the Java language due to the complexities that arise. Python makes then usable with the "super" construct and the MRO concept - but if you need different keywords in the initializers of different superclasses, each class has to know how to deal with the arguements it does not know about.

So Hybrid is to be called with both a "name", a "country" and a "planet" argument - but Transformers don't know about "country", nor Gundams know about "planet".

In Python you can handle that using Keyword arguments - you have to make your intermediary classes accept all kinds of keyword arguments, and use the ones it knows about - in this case, it is as simply as changing the classes' __init__ signature in this way, and make the proper adjust to the super calls:

class Robot(object):
    """A robot class"""
    def __init__(self, name, **kwargs):
        ...

class Transformer(Robot):
    """A transformer"""
    def __init__(self, name, planet, **kwargs):
        ...      
        super(Transformer, self).__init__(name, **kwargs)

class Gundam(Robot):
    """A Gundam"""
    def __init__(self, name, country, **kwargs):
        ...      
        super(Gundam, self).__init__(name, **kwargs)
    ...

And of course, when instatiating an Hybrid, you now have to name the parameters:

my_robot = Hybrid("XYZ-Star", planet="Earth", country="Japan")

2 Comments

You might want to have the Transformer and Gundam classes handle name as a keyword argument, since they don't use it themselves at all. That means, either use name=name when passing it on, or simply not taking it as a named parameter in the first place (so it will be part of kwargs).
@jsbueno Yes I agree with this post. The kwargs trick results in cleaner code with less chance of confusion by keeping mention of Planet out of the Gundam Class and Country out of the Transformer Class. All working now. I have added a 'str' to **Hybrid"" 'class ---

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.