user15570208
user15570208

Reputation:

How to handle parameters with polymorphism in python?

I want to use polymorphism and have a Movil class as my parent class, and while specific class is Car:

class Movil:
    def __init__(self, name):
        self._name = name

class Car(Movil):
    def __init__(self, code):
        super().__init__()
        self.code = code

Since every Movil takes a name and every Movil takes a code and is a car, I expect to be able to pass both:

class Main(object):
    def main(self):
        a=Car('toyota','001')

if __name__ == "__main__":
    Main().main()

But I am getting this error:

Traceback (most recent call last):
  File "main", line 1, in <module>
  File "main", line 3, in main
TypeError: __init__() takes 2 positional arguments but 3 were given

What is wrong with this code?

Upvotes: 1

Views: 1062

Answers (1)

MisterMiyagi
MisterMiyagi

Reputation: 50116

TLDR: Method parameters are not "inherited" when a child class overrides a parent method. The child class method must explicitly take and forward the parameter:

class Car(Movil):
    def __init__(self, name, code):
        super().__init__(name)
        self.code = code

Inheritance only integrates attributes and methods of the base class into the child class. Notably, if the child class redefines an attribute or method, this hides ("shadows") the parent attribute/method completely.

For example, if Car would not define its own __init__ then Movil.__init__ would be used. Related and derived features – such as "the parameters of __init__" – are not themselves inherited: they only show up because they belong to the inherited attribute/method.
Since Car does define its own __init__, this shadows Movil.__init__ including its related features, such as the parameters.

In order for Car to take the original name parameter, it must be explicitly re-defined on Car.__init__:

class Car(Movil):
    #                  v take `name` parameter of Movil.__init__
    def __init__(self, name, code):
        #                v pass on `name` parameter to Movil.__init__
        super().__init__(name)
        self.code = code

As an alternative, variadic positional (*args) or keyword (**kwargs) parameters may be used to forward all unknown arguments:

class Car(Movil):
    #                        v collect unknown arguments
    def __init__(self, code, *args, **kwargs):
        #                v pass on unknown arguments to Movil.__init__
        super().__init__(*args, **kwargs)
        self.code = code

a = Car("001", "toyota")
b = Car(name="toyota", code="001")

Be mindful that variadic parameters make it difficult or impossible to replicate some patterns using positional-or-keyword parameters. For example, in the above example it is not possible to accept code as both a keyword or trailing positional argument, as is possible with the explicit definition.

Upvotes: 4

Related Questions