20

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.

Landon Poch
  • 832
  • 1
  • 8
  • 19
  • 2
    If you're concerned about someone calling `super.__init__`, or want it to do something but not be *directly* instantiable, you could have something like this in `Base.__init__`: `if type(self) is Base: raise NotImplementedError('Base.__init__(): abstract class')` – Chris Morgan Feb 20 '13 at 06:26

6 Answers6

24

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.'
Blckknght
  • 100,903
  • 11
  • 120
  • 169
5

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.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • +1 because I think this is usually the right way to go. However I had to give the answer to @Blckknght because he was a little more explicit about how to have an implementation on the base that can be called only from a subtype. This could be useful especially on constructors where you might want some shared logic on the base `__init__` method. – Landon Poch Feb 20 '13 at 15:27
3

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.

John Mee
  • 50,179
  • 34
  • 152
  • 186
  • 7
    Be very cautious with `return NotImplemented`. It has a very specific intended use case. c.f. http://stackoverflow.com/questions/1062096/python-notimplemented-constant and the [`NotImplemented` description in the docs](http://docs.python.org/2/library/constants.html#NotImplemented). Normally you will want to `raise NotImplementedError()`. – Chris Morgan Feb 20 '13 at 06:25
3

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.

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
  • 2
    A more common Python convention is "Base". "BaseXYZ" classes are typically not intended to be instantiable. (The two cases which immediately spring to my mind where "Base" is used are Django models and Python's built-in HTTP server.) – Chris Morgan Feb 20 '13 at 06:27
  • @ChrisMorgan And `basestring` - throws a `TypeError: The basestring type cannot be instantiated` – Alex L Feb 20 '13 at 06:38
  • @ChrisMorgan Some related discussion here: http://mail.python.org/pipermail/python-dev/2003-November/039916.html – Alex L Feb 20 '13 at 06:40
1

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.

Neuron
  • 5,141
  • 5
  • 38
  • 59
0

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.

Ellioh
  • 5,162
  • 2
  • 20
  • 33