Python abstract classes – how to discourage instantiation?

Question:

I come from a C# background where the language has some built in “protect the developer” features. I understand that Python takes the “we’re all adults here” approach and puts responsibility on the developer to code thoughtfully and carefully.

That said, Python suggests conventions like a leading underscore for private instance variables. My question is, is there a particular convention for marking a class as abstract other than just specifying it in the docstrings? I haven’t seen anything in particular in the python style guide that mentions naming conventions for abstract classes.

I can think of 3 options so far but I’m not sure if they’re good ideas:

  1. Specify it in the docstring above the class (might be overlooked)
  2. Use a leading underscore in the class name (not sure if this is universally understood)
  3. Create a def __init__(self): method on the abstract class that raises an error (not sure if this negatively impacts inheritance, like if you want to call a base constructor)

Is one of these a good option or is there a better one? I just want to make sure that other developers know that it is abstract and so if they try to instantiate it they should accept responsibility for any strange behavior.

Asked By: Landon Poch

||

Answers:

Based on your last sentence, I would answer answer “just document it”. Anyone who uses a class in a way that the documentation says not to must accept responsibility for any strange behavior.

There is an abstract base class mechanism in Python, but I don’t see any reason to use it if your only goal is to discourage instantiation.

Answered By: BrenBarn

Create your ‘abstract’ class and raise NotImplementedError() in the abstract methods.

It won’t stop people using the class and, in true duck-typing fashion, it will let you know if you neglect to implement the abstract method.

Answered By: John Mee

I just name my abstract classes with the prefix ‘Abstract’. E.g. AbstractDevice, AbstractPacket, etc.

It’s about as easy and to the point as it comes. If others choose to go ahead and instantiate and/or use a class that starts with the word ‘Abstract’, then they either know what they’re doing or there was no hope for them anyway.

Naming it thus, also serves as a reminder to myself not to go nuts with deep abstraction hierarchies, because putting ‘Abstract’ on the front of a whole lot of classes feels stupid too.

Answered By: Travis Griggs

If you’re using Python 2.6 or higher, you can use the Abstract Base Class module from the standard library if you want to enforce abstractness. Here’s an example:

from abc import ABCMeta, abstractmethod

class SomeAbstractClass(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def this_method_must_be_overridden(self):
        return "But it can have an implementation (callable via super)."

class ConcreteSubclass(SomeAbstractClass):
    def this_method_must_be_overridden(self):
        s = super(ConcreteSubclass, self).this_method_must_be_overridden()
        return s.replace("can", "does").replace(" (callable via super)", "")

Output:

>>> a = SomeAbstractClass()
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    a = SomeAbstractClass()
TypeError: Can't instantiate abstract class SomeAbstractClass with abstract
methods this_method_must_be_overridden
>>> c = ConcreteSubclass()
>>> c.this_method_must_be_overridden()
'But it does have an implementation.'
Answered By: Blckknght

To enforce things is possible, but rather unpythonic. When I came to Python after many years of C++ programming I also tried to do the same, I suppose, most of people try doing so if they have an experience in more classical languages. Metaclasses would do the job, but anyway Python checks very few things at compilation time. Your check will still be performed at runtime. So, is the inability to create a certain class really that useful if discovered only at runtime? In C++ (and in C# as well) you can not even compile you code creating an abstract class, and that is the whole point — to discover the problem as early as possible. If you have abstract methods, raising a NotImplementedError exception seems to be quite enough. NB: raising, not returning an error code! In Python errors usually should not be silent unless thay are silented explicitly. Documenting. Naming a class in a way that says it’s abstract. That’s all.

Quality of Python code is ensured mostly with methods that are quite different from those used in languages with advanced compile-time type checking. Personally I consider that the most serious difference between dynamically typed lngauges and the others. Unit tests, coverage analysis etc. As a result, the design of code is quite different: everything is done not to enforce things, but to make testing them as easy as possible.

Answered By: Ellioh

In Python 3.x, your class can inherit from abc.ABC.
This will make your class non-instantiable and your IDE will warn you if you try to do so.

import abc

class SomeAbstractClass(abc.ABC):
    @abc.abstractmethod
    def some_abstract_method(self):
        raise NotImplementedError
    
    @property
    @abc.abstractmethod
    def some_abstract_property(self):
        raise NotImplementedError

This has first been suggested in PEP 3119.

Answered By: Neuron