I agree with TooAngel, but I'd use the __new__ method.

class Shape(object):
    def __new__(cls, *args, **kwargs):
        if cls is Shape:                            # <-- required because Line's
            description, args = args[0], args[1:]   #     __new__ method is the
            if description == "It's flat":          #     same as Shape's
                new_cls = Line
            else:
                raise ValueError("Invalid description: {}.".format(description))
        else:
            new_cls = cls
        return super(Shape, cls).__new__(new_cls, *args, **kwargs)

    def number_of_edges(self):
        return "A shape can have many edges…"

class Line(Shape):
    def number_of_edges(self):
        return 1

class SomeShape(Shape):
    pass

>>> l1 = Shape("It's flat")
>>> l1.number_of_edges()
1
>>> l2 = Line()
>>> l2.number_of_edges()
1
>>> u = SomeShape()
>>> u.number_of_edges()
'A shape can have many edges…'
>>> s = Shape("Hexagon")
ValueError: Invalid description: Hexagon.
Answer from Georg Schölly on Stack Overflow
🌐
Python
docs.python.org › 3 › library › abc.html
abc — Abstract Base Classes
The __subclasshook__() class method defined here says that any class that has an __iter__() method in its __dict__ (or in that of one of its base classes, accessed via the __mro__ list) is considered a MyIterable too. Finally, the last line makes Foo a virtual subclass of MyIterable, even though it does not define an __iter__() method (it uses the old-style iterable protocol, defined in terms of __len__() and __getitem__()).
🌐
Python documentation
docs.python.org › 3 › tutorial › classes.html
9. Classes — Python 3.14.3 documentation
Because methods have no special privileges when calling other methods of the same object, a method of a base class that calls another method defined in the same base class may end up calling a method of a derived class that overrides it. (For C++ programmers: all methods in Python are effectively virtual.)
🌐
Medium
medium.com › @s.martinallo › abstract-virtual-classes-with-python-48bf60d00d9e
Abstract (virtual) Classes with Python | by Stefano Martinallo | Medium
January 29, 2024 - Abstract classes allow developers to define a high-level behavior of an object by delegating the details and the implementation to a concrete instance (typically a subclass). In this post I am going to share my experience with the Python Abstract Base Class library used in an NMEA parser I wrote myself.
Top answer
1 of 9
136

Sure, and you don't even have to define a method in the base class. In Python methods are better than virtual - they're completely dynamic, as the typing in Python is duck typing.

class Dog:
  def say(self):
    print "hau"

class Cat:
  def say(self):
    print "meow"

pet = Dog()
pet.say() # prints "hau"
another_pet = Cat()
another_pet.say() # prints "meow"

my_pets = [pet, another_pet]
for a_pet in my_pets:
  a_pet.say()

Cat and Dog in Python don't even have to derive from a common base class to allow this behavior - you gain it for free. That said, some programmers prefer to define their class hierarchies in a more rigid way to document it better and impose some strictness of typing. This is also possible - see for example the abc standard module.

2 of 9
124

Summary of current implementation status with a focus on "blowup nicely if the method is not implemented" behavior:

methd exception mypy static sphinx
raise NotImplementedError() on method call n n
typing.Protocol n y n
@abc.abstractmethod on instantiation y y

raise NotImplementedError(): dynamic type checking

This is the recommended exception to raise on "pure virtual methods" of "abstract" base classes that don't implement a method.

https://docs.python.org/3.5/library/exceptions.html#NotImplementedError says:

This exception is derived from RuntimeError. In user defined base classes, abstract methods should raise this exception when they require derived classes to override the method.

As others said, this is mostly a documentation convention and is not required, but this way you get a more meaningful exception than a missing attribute error.

dynamic.py

class Base(object):
    def virtualMethod(self):
        raise NotImplementedError()
    def usesVirtualMethod(self):
        return self.virtualMethod() + 1

class Derived(Base):
    def virtualMethod(self):
        return 1

assert Derived().usesVirtualMethod() == 2
Base().usesVirtualMethod()

gives:

Traceback (most recent call last):
  File "./dynamic.py", line 13, in <module>
    Base().usesVirtualMethod()
  File "./dynamic.py", line 6, in usesVirtualMethod
    return self.virtualMethod() + 1
  File "./dynamic.py", line 4, in virtualMethod
    raise NotImplementedError()
NotImplementedError

typing.Protocol: static type checking (Python 3.8)

Python 3.8 added typing.Protocol which now allows us to also statically type check that a virtual method is implemented on a subclass.

protocol.py

from typing import Protocol

class Bird(Protocol):
    def fly(self) -> str:
        pass

    def peck(self) -> str:
        return 'Bird.peck'

class Pigeon(Bird):
    def fly(self):
        return 'Pigeon.fly'

    def peck(self):
        return 'Pigeon.peck'

class Parrot(Bird):
    def fly(self):
        return 'Parrot.fly'

class Dog(Bird):
    pass

pigeon = Pigeon()
assert pigeon.fly() == 'Pigeon.fly'
assert pigeon.peck() == 'Pigeon.peck'
parrot = Parrot()
assert parrot.fly() == 'Parrot.fly'
assert parrot.peck() == 'Bird.peck'
# mypy error
dog = Dog()
assert dog.fly() is None
assert dog.peck() == 'Bird.peck'

If we run this file, the asserts pass, as we didn't add any dynamic typechecking:

python protocol.py

but if we typecheck if mypy:

python -m pip install --user mypy
mypy protocol.py

we get an error as expected:

rotocol.py:31: error: Cannot instantiate abstract class "Dog" with abstract attribute "fly"  [abstract]
Found 1 error in 1 file (checked 1 source file)

It is a bit unfortunate however that the error checking only picks up the error on instantiation, and not at class definition.

typing.Protocol counts methods as abstract when their body is "empty"

I'm not sure what they count as empty, but both all of the following count as empty:

  • pass
  • ... ellipsis object
  • raise NotImplementedError()

So the best possibility is likely:

protocol_empty.py

from typing import Protocol

class Bird(Protocol):
    def fly(self) -> None:
        raise NotImplementedError()

class Pigeon(Bird):
    def fly(self):
        return None

class Dog(Bird):
    pass

Bird().fly()
Dog().fly()

which fails as desired:

protocol_empty.py:14: error: Cannot instantiate protocol class "Bird"  [misc]
protocol_empty.py:15: error: Cannot instantiate abstract class "Dog" with abstract attribute "fly"  [abstract]
protocol_empty.py:15: note: "fly" is implicitly abstract because it has an empty function body. If it is not meant to be abstract, explicitly `return` or `return None`.
Found 2 errors in 1 file (checked 1 source file)

but if e.g. we replace the:

raise NotImplementedError()

with some random "non-empty" statement such as:

x = 1

then mypy does not count them as virtual and gives no errors.

@abc.abstractmethod: dynamic + static + documentation in one go

Previously mentioned at: https://stackoverflow.com/a/19316077/895245 but the metaclass syntax changed in Python 3 to:

class C(metaclass=abc.ABCMeta):

instead of the Python 2:

class C:
    __metaclass__=abc.ABCMeta

so now to use @abc.abstractmethod which was previously mentioned at https://stackoverflow.com/a/19316077/895245 you need:

abc_cheat.py

import abc

class C(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def m(self, i):
        pass

try:
    c = C()
except TypeError:
    pass
else:
    assert False

Vs raise NotImplementedError and Protocol:

  • disadvantage: more verbose
  • advantage: does all of dynamic checks, static checks and shows up on documentation (see below)

https://peps.python.org/pep-0544 does mention both approaches in passing

E.g.:

abc_bad.py

#!/usr/bin/env python

import abc

class CanFly(metaclass=abc.ABCMeta):
    '''
    doc
    '''

    @abc.abstractmethod
    def fly(self) -> str:
        '''
        doc
        '''
        pass

class Bird(CanFly):
    '''
    doc
    '''

    def fly(self):
        '''
        doc
        '''
        return 'Bird.fly'

class Dog(CanFly):
    '''
    doc
    '''
    pass

def send_mail(flyer: CanFly) -> str:
    '''
    doc
    '''
    return flyer.fly()

assert send_mail(Bird()) == 'Bird.fly'
assert send_mail(Dog()) == 'Dog.fly'

then:

mypy abc_bad.py

fails as desired with:

main.py:40: error: Cannot instantiate abstract class "Dog" with abstract attribute "fly"

Sphinx: make it show on the documentation

See: How to annotate a member as abstract in Sphinx documentation?

Of the methods mentioned above, only one shows up on the sphinx documentation output: @abc.abstractmethod.

Outro

Bibiography:

  • https://peps.python.org/pep-0544 the typing.Protocol PEP
  • Is it possible to make abstract classes in Python?
  • What to use in replacement of an interface/protocol in python

Tested on Python 3.10.7, mypy 0.982, Ubuntu 21.10.

🌐
DEV Community
dev.to › dollardhingra › understanding-the-abstract-base-class-in-python-k7h
Beginner's guide to abstract base class in Python - DEV Community
June 1, 2021 - We get the error if we try to call the base class' method. But, we can instantiate the base class. So, the above program doesn't follow Rule 2 · With Python's abc module, we can do better and make sure that both the rules are followed.
🌐
Python
legacy.python.org › workshops › 1998-11 › proceedings › papers › lowis › lowis.html
Virtual Method Tables in Python
Virtual tables are a mechanism to find methods of a class efficiently. Typically, they are used for statically-typed languages. This paper discusses an implementation of this mechanism for Python.
Find elsewhere
🌐
Real Python
realpython.com › lessons › using-metaclasses-vbc
Using Metaclasses and VBC (Video) – Real Python
05:08 Here, you have the setup for creating your virtual base classes. The metaclass PersonMeta the base class PersonSuper and the Python interface Person.
Published   June 4, 2024
🌐
Reddit
reddit.com › r/learnpython › good online virtual classes to learn python?
r/learnpython on Reddit: Good online virtual classes to learn Python?
August 15, 2023 -

Looking to learn python, but I'm definitely someone who functions better in a structured (almost classroom-like) environment over self-paced learning. Any recommendations for live virtual classes to learn Python?

🌐
I2ctraining
i2ctraining.com › home › instructor led virtual live courses › python: virtual live class
Python: Virtual Live Class – i2ctraining.com
Python: Virtual Live Class
The course will lead you from beginning level to advance in Python Programming Language. You do not need any prior knowledge on Python or any programming language or even programming to join the course and become an expert on the topic. Our Python training online is regarded as the one of the Best online training. Target Students / Prerequisites: You do not need any prior knowledge on Python Benefits from this course: Job oriented training Visual learning Real world assignments Recorded sessions Mock Interviews Resume preparation Supporting Materials Job Supp
Price   $650.00
Top answer
1 of 2
40

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>
2 of 2
12

Problem is that this error is found at runtime!

Well, it is Python... most errors are going to show up at runtime.

As far as I know, the most common pattern to deal with is in Python is basically what you describe: just have the base class's speak method throw an exception:

class Animal():
    def speak(self):
        raise NotImplementedError('You need to define a speak method!')
🌐
pybind11
pybind11.readthedocs.io › en › stable › advanced › classes.html
Classes - pybind11 documentation
Defining a new type of Animal from within Python is possible but requires a helper class that is defined as follows: class PyAnimal : public Animal, public py::trampoline_self_life_support { public: /* Inherit the constructors */ using Animal::Animal; /* Trampoline (need one for each virtual function) */ std::string go(int n_times) override { PYBIND11_OVERRIDE_PURE( std::string, /* Return type */ Animal, /* Parent class */ go, /* Name of function in C++ (must match Python name) */ n_times /* Argument(s) */ ); } };
🌐
GitHub
github.com › pybind › pybind11 › issues › 2844
[QUESTION] Implements a pure virtual class in python calls pure virtual function · Issue #2844 · pybind/pybind11
January 2, 2021 - The issue here is that in python, I need to keep the python object around or I would have a pure virtual function call error. ... class BlendingField(PyVNCS.Sim2D.BlendingField): def __init__(self): PyVNCS.Sim2D.BlendingField.__init__(self) def blending(self, point): return 0.5
Author   jjcasmar
🌐
Teach-technology
teach-technology.org › virtual-python-classes
Virtual Python Classes
Teach-Technology Organization, and its Python instructors have decided to host our Python Programming Classes for Middle and High School students for free online!
Top answer
1 of 1
48

I read Interfaces in Python: Protocols and ABCs and it gives me a better understanding. We have duck typing in Python:

If it talks and walks like a duck, then it is a duck.

However, a Bird and Aeroplane both can fly(). But they are not the same thing. Hence, we need to define an interface to distinguish them from each other. (Python does not have an interface keyword, so we are actually using abstract classes)

Let's me show an example:

We have Duck and MyPlane in our program. Both of them implemented fly() method. Now we want to choose a plane from the hangar, get some people on board, and fly to another city. Apparently, we cannot put people onto a Duck, so we define an interface called (actually, an abstract class) Plane. And we let MyPlane to subclass Plane.

Everything works fine. When we want to choose a plane, we check if it subclasses Plane. However, the Boeing company developed a package, which has a Boeing747Plane. We bought the plane (from boeing_airplanes import Boeing747Plane), but it is not recognized as a plane. It does have a fly() method, but it's not inherited from our Plane class so our Python interpreter won't recognize it as a plane.

The good news is that Python is a flexible language. Thanks for register method of ABCMeta, after we do Plane.register(Boeing747Plane), Boeing747Plane is a subclass of Plane now. We can use third-party Boeing747Plane like our own built Plane. Hooray!

So you see, virtual classes are used when we want to make a class from a third-party package to be a subclass of our own abstract class. We want it to implement our interface, but we cannot change its code, so we tell the interpreter explicitly "it implemented our interface, please treat it as the subclass of our own class". I think normally we wouldn't want to use it, but when you need to, use it cautiously. As Luca Cappelletti said, this is one of many flexibilities that Python allows for, following its philosophy of "we are adults here".

🌐
X
engrprogrammer.com › how-to-implement-virtual-methods-in-python-step-by-step-guide
How to Implement Virtual Methods in Python: Step by ...
JavaScript is not available · We’ve detected that JavaScript is disabled in this browser. Please enable JavaScript or switch to a supported browser to continue using x.com. You can see a list of supported browsers in our Help Center · Help Center · Terms of Service Privacy Policy Cookie ...
🌐
Python
wiki.python.org › moin › boost.python › OverridableVirtualFunctions
boost.python/OverridableVirtualFunctions - Python Wiki
You may ask, "Why do we need this derived class? This could have been designed so that everything gets done right inside of hello." One of the goals of Boost.Python is to be minimally intrusive on an existing C++ design. In principle, it should be possible to expose the interface for a 3rd party library without changing it. To unintrusively hook into the virtual functions so that a Python override may be called, we must use a derived class.