maxime maxime - 6 months ago 11
Python Question

The necessity of having unimplemented methode in abstract class for sake of polymorphism?

I don't see it is important to have it, because the real implementations will be implemented in each subclass anyway. Consider this code :

class Cat:
def __init__(self, breed):
self.breed = breed
def sound(self): # having this or not does'nt make any difference
pass

class Angora(Cat):
def sound(self):
print("miau")

class Scotish(Cat):
def sound(self):
print('meowww')

a = Angora('angora')
b = Scotish('scotish')

cats = [a, b]

for catt in cats:
catt.sound()


I must be missing something right now because i am having difficulty to grasp this term.

Answer

placing an empty method that will get overridden, no there is not much point, but if Cat was an actual abstract class and sound was a method that was inforced to be overriden, then there is a big benefit from it, for example:

import abc

class Cat(abc.ABC):
    def __init__(self, breed):
        self.breed = breed
    @abc.abstractmethod
    def sound(self): # having this as an abstract method DOES make a difference
        pass

class Scotish(Cat):
    def sonud(self):
        print('meowww')

Whether you noticed the typo or not, you will get this error when trying to initialize a Scotish:

>>> b = Scotish("scotish")
Traceback (most recent call last):
  File "<pyshell#12>", line 1, in <module>
    b = Scotish("scotish")
TypeError: Can't instantiate abstract class Scotish with abstract methods sound

So then before you get any weird ambiguous errors you are given what actually went wrong right away!

Also unlike simply raise NotImplementedError an abstract method can contain, well abstract code! Like an example of what the function might do, a great example would be _collections_abc.Generator.throw:

@abstractmethod
def throw(self, typ, val=None, tb=None):
    """Raise an exception in the coroutine.
    Return next yielded value or raise StopIteration.
    """
    if val is None:
        if tb is None:
            raise typ
        val = typ()
    if tb is not None:
        val = val.with_traceback(tb)
    raise val

This is not real code, this is not how a generator actually handles the throw method but it is a great abstract of how to at least handle the arguments!