Are Python pure virtual functions possible and/or worth it?

benzeno picture benzeno · Oct 20, 2014 · Viewed 21.5k times · Source

I may be coming from a different mindset, being primarily a C++ programmer. This question has to do with OOP in Python and more specifically pure virtual methods. So taking code I adapted from this question I am looking at this basic sample.

class Animal():
    def speak(self):
        print("...")

class Cat(Animal):
    def speak(self):
        print("meow")

class Dog(Animal):
    def speak(self):
        print("woof")

my_pets = [Dog(), Cat(), Dog()]

for _pet in my_pets:
     _pet.speak()

So you see it calls the speak function for different derived classes. Now my problem is that duck typing is all good and I think I have grasped it. However, is it wrong to pursue more strict OOP in Python? So I looked at the Abstract Base Classes and specifically abstractmethod. To me all this seems to do is allow me to call the base class method with super. Is there any way/reason(in Python) to make speak() pure such that implementing a derived animal without speak would throw an error?

My argument for such a pursuit would be when writing modules and frameworks that you intend people to subclass, this would self document for them the fact that they need implement the function. A probably very bad idea is something like this, having the base class "pure" function throw an exception. Problem is that this error is found at runtime!

class VirtualException(BaseException):
    def __init__(self, _type, _func):
        BaseException(self)

class Animal():
    def speak(self):
        raise VirtualException()

class Cat(Animal):
    def speak(self):
        print("meow")

class Dog(Animal):
    def speak(self):
        print("woof")

class Wildebeest(Animal):
    def function2(self):
        print("What!")

my_pets = [Dog(), Cat(), Dog(), Wildebeest()]

for _pet in my_pets:
    _pet.speak()

Answer

user2357112 supports Monica picture user2357112 supports Monica · Oct 20, 2014

Abstract base classes already do what you want. abstractmethod has nothing to do with letting you call the method with super; you can do that anyway. Instead, any methods decorated with abstractmethod must be overridden for a subclass to be instantiable:

Python 3:

>>> class Foo(metaclass=abc.ABCMeta):
...     @abc.abstractmethod
...     def foo(self):
...         pass
...
>>> class Bar(Foo):
...     pass
...
>>> class Baz(Bar):
...     def foo(self):
...         return super(Baz, self).foo()
...
>>> Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods foo
>>> Bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Bar with abstract methods foo
>>> Baz()
<__main__.Baz object at 0x00000210D702E2B0>

Python 2:

>>> class Foo(object):
...     __metaclass__ = abc.ABCMeta
...     @abc.abstractmethod
...     def foo(self): pass
...
>>> class Bar(Foo): pass
...
>>> class Baz(Bar):
...     def foo(self): return super(Baz, self).foo()
...
>>> Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods foo
>>> Bar()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Bar with abstract methods foo
>>> Baz()
<__main__.Baz object at 0x0000000001EC10B8>