user9814172
user9814172

Reputation:

Declaring Subclass without passing self

I have an abstract base class Bicycle:

from abc import ABC, abstractmethod

class Bicycle(ABC):

    def __init__(self, cadence = 10, gear = 10, speed = 10):
        self._cadence = cadence
        self._gear = gear         
        self._speed = speed

    @abstractmethod
    def ride(self):
        pass

    def __str__(self):
        return  "Cadence: {0}  Gear: {1}  Speed: {2}".format(self._cadence, 
                                                             self._gear, self._speed)

and a subclass MountainBike:

from Bicycle import Bicycle

class MountainBike(Bicycle):

    def __init__(self):
        super().__init__(self)


    def ride(self):
        return "Riding my Bike"

The following code will cause a recursion error, but if I remove self from the super().__init__(self), the call to __str__(self): works.

Question:

  1. I only discovered this error when I implemented the __str__(self):

    In Python 3.x when calling the parent constructor from the child with no arguments, is passing self, necessary?

  2. Suppose MountainBike now sets the cadence, gear, speed this means in my subclass the constructor will look like this:

    class MountainBike(Bicycle):
    
        def __init__(self, cadence, gear, speed):
            super().__init__(cadence,gear,speed)
    

notice, self isn't being passed in the super because to my knowledge, it can throw the variable assignments off. Is this assumption correct?

Upvotes: 6

Views: 124

Answers (1)

zvone
zvone

Reputation: 19352

self is passed implicitly to the super call, so adding it explicitly sends it twice:

def __init__(self):
    super().__init__(self)

That ends up calling Bicycle(self, self), which is the same as Bicycle(self, cadence=self).

Later on, you have probably tried convert your instance to str (e.g. to print it), so this was called:

def __str__(self):
    return  "Cadence: {0}  Gear: {1}  Speed: {2}".format(self._cadence, 
                                                         self._gear, self._speed)

That code tried to convert self._cadence to a string and self._cadence is self because of the previous error, so it continues in an endless recursion (until the recursion exception).


Note that super() takes two forms: with arguments and without arguments, so there are two correct ways to fix the code.

The Python 3 way (without arguments):

def __init__(self):
    super().__init__()

The old Python 2 way, which is more explicit:

def __init__(self):
    super(MountainBike, self).__init__()

Both do the same, i.e. they give you the bound __init__ method which already has the implicit self.

See also here: https://docs.python.org/3/library/functions.html#super

Upvotes: 3

Related Questions