57

In Python 2.x when you want to mark a method as abstract, you can define it like so:

class Base:
    def foo(self):
        raise NotImplementedError("Subclasses should implement this!")

Then if you forget to override it, you get a nice reminder exception. Is there an equivalent way to mark a field as abstract? Or is stating it in the class docstring all you can do?

At first I thought I could set the field to NotImplemented, but when I looked up what it's actually for (rich comparisons) it seemed abusive.

Kiv
  • 31,940
  • 6
  • 44
  • 59
  • It still works, even if it's original intent was for rich comparisons. What's wrong with it? – S.Lott Jul 20 '09 at 00:08
  • 1
    The first problem is you can read the field from the object (myvar = Base.field) and next thing you know there are NotImplementeds all over the place until some other part tries to use it and gets a mysterious AttributeError. – Kiv Jul 20 '09 at 00:22
  • The second problem is that IMO it hampers readability ("What's that rich comparison thing doing there? Did I miss something?) Evan's solution expresses exactly what is going on in a familiar way. – Kiv Jul 20 '09 at 00:23
  • @Kiv: Please do not comment on your question. Please update your question with the specific points you're raising. – S.Lott Jul 20 '09 at 02:23
  • Related: http://stackoverflow.com/questions/2736255/abstract-attributes-in-python – guettli Jun 16 '16 at 14:32

8 Answers8

60

Yes, you can. Use the @property decorator. For instance, if you have a field called "example" then can't you do something like this:

class Base(object):

    @property
    def example(self):
        raise NotImplementedError("Subclasses should implement this!")

Running the following produces a NotImplementedError just like you want.

b = Base()
print b.example
Evan Fosmark
  • 98,895
  • 36
  • 105
  • 117
  • 4
    This is simpler, but I like how my version throws immediately and not only if the attribute happens to be used. – Glenn Maynard Jul 20 '09 at 00:10
  • 4
    But Glenn, what if the property `example` got set at some other point? If it throws it immediately, then it may never get a chance to be set through other means. Remember that fields and methods can be set to a class at any time and not just when the class is defined. – Evan Fosmark Jul 20 '09 at 00:16
  • Ah, this is what I was after. – Kiv Jul 20 '09 at 00:18
  • 3
    If I'm defining a base class which expects a method (or field) to be defined by the user, I expect it to be defined at all times when the base class is active, from __init__ onward. I'd consider defining it later to be an error, because I might want to access them from __init__. You can define your classes with different rules, of course, but this seems the clearest. (Of course, as I'm originally a C++ programmer, I like nice, strict, well-defined interface rules.) – Glenn Maynard Jul 20 '09 at 00:28
  • Goofy comment system strikes again. I'm sure you can figure out what I meant. :| – Glenn Maynard Jul 20 '09 at 00:29
  • 3
    @Glenn, two comments up: that sounds like the C++ philosophy, whereas the Python philosophy is much looser - you only care about what you get when you try to access a property. When you're not trying to access it, it can be anything, including undefined. (Of course this is no requirement, it's just the way a lot of Python code is written, and I think it's the intent of the language creators/maintainers that it be that way.) – David Z Jul 20 '09 at 00:44
36

Alternate answer:

@property
def NotImplementedField(self):
    raise NotImplementedError

class a(object):
    x = NotImplementedField

class b(a):
    # x = 5
    pass

b().x
a().x

This is like Evan's, but concise and cheap--you'll only get a single instance of NotImplementedField.

Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
  • 3
    Clever, Glenn. :) The only downside I can see is that you can't specify different messages to be shown when NotImplementedError gets thrown. – Evan Fosmark Jul 20 '09 at 00:20
  • 2
    You could define NotImplementedField as a function taking a message to display. You'd have to get a little clever to keep it using a single instance of the function when no message is attached--cache a singleton for no message--but that's about it. – Glenn Maynard Jul 20 '09 at 00:25
  • @umbrae: See my previous comment (of two years ago); that's easy to implement if you want it. – Glenn Maynard Nov 13 '11 at 23:34
7

A better way to do this is using Abstract Base Classes:

import abc

class Foo(abc.ABC):

    @property
    @abc.abstractmethod
    def demo_attribute(self):
        raise NotImplementedError

    @abc.abstractmethod
    def demo_method(self):
        raise NotImplementedError

class BadBar(Foo):
    pass

class GoodBar(Foo):

    demo_attribute = 'yes'

    def demo_method(self):
        return self.demo_attribute

bad_bar = BadBar()
# TypeError: Can't instantiate abstract class BadBar \
# with abstract methods demo_attribute, demo_method

good_bar = GoodBar()
# OK

Note that you should still have raise NotImplementedError instead of something like pass, because there is nothing preventing the inheriting class from calling super().demo_method(), and if the abstract demo_method is just pass, this will fail silently.

ostrokach
  • 17,993
  • 11
  • 78
  • 90
  • 1
    What is the difference between `raise NotImplementedError` and `raise NotImplementedError(something)`. On the face of it, the former raises a class, and the latter raises an instance. Is that ambiguity an issue / problem? – Reb.Cabin Jun 05 '18 at 15:28
  • 3
    @Reb.Cabin Both are valid. Typically, you would raise `NotImplementedError` when you are not providing any arguments and `raise NotImplementedError("something")` if you are providing arguments. See: https://stackoverflow.com/a/16709222/2063031. – ostrokach Jun 05 '18 at 15:46
  • 1
    I'm curious - why is it better to use an abstract base class? Are there any additional benefits? Otherwise this appears on the surface to have a few extra lines and requires importing an additional class to achieve the same result. – YPCrumble Apr 28 '20 at 19:26
  • 1
    @YPCrumble I think the main benefit is that you get an error when you try to instantiate a class which does not override all the abstract methods and properties, as opposed to getting an exception at runtime when one of the NotImplemented methods is called. In practice, whether or not this makes a difference probably depends on your specific application. – ostrokach Apr 28 '20 at 20:05
  • You're raising a `NotImplementedError` class, instead of an instance. – Sander Feb 04 '23 at 15:05
  • @Sander Either raising an instance or a class is valid: https://docs.python.org/3/tutorial/errors.html#raising-exceptions. – ostrokach Feb 08 '23 at 20:28
  • @ostrokach Valid, but not clean. Using `not True` also has similar semantics to `False`. I don't think its good style (and this Python laxity is not transferable to other programming languages). – Sander Feb 11 '23 at 09:47
2
def require_abstract_fields(obj, cls):
    abstract_fields = getattr(cls, "abstract_fields", None)
    if abstract_fields is None:
        return

    for field in abstract_fields:
        if not hasattr(obj, field):
            raise RuntimeError, "object %s failed to define %s" % (obj, field)

class a(object):
    abstract_fields = ("x", )
    def __init__(self):
        require_abstract_fields(self, a)

class b(a):
    abstract_fields = ("y", )
    x = 5
    def __init__(self):
        require_abstract_fields(self, b)
        super(b, self).__init__()

b()
a()

Note the passing of the class type into require_abstract_fields, so if multiple inherited classes use this, they don't all validate the most-derived-class's fields. You might be able to automate this with a metaclass, but I didn't dig into that. Defining a field to None is accepted.

Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
1

It seem that this question was open to both instance attributes and class attributes, I'll focus on the first topic only.

So, for instance attributes, an alternate answer to Evan's is to define a mandatory field using pyfields:

from pyfields import field

class Base(object):
    example = field(doc="This should contain an example.")

b = Base()
b.example

yields

pyfields.core.MandatoryFieldInitError: 
   Mandatory field 'example' has not been initialized yet 
   on instance <__main__.Base object at 0x000002C1000C0C18>.

Granted, it does not provide you with the ability to edit the error message by talking about subclasses. But in a way it is more realistic to not talk about subclasses - indeed in python, attributes can be overridden on instances of the base class - not only in subclasses.

Note: I'm the author of pyfields. See documentation for details.

smarie
  • 4,568
  • 24
  • 39
1

Here is a simple example how to set required properties/methods for sublasses in Python 3.

class Base:
    requires = ('foo', 'bar')

    def __init_subclass__(cls, **kwargs):
        for requirement in cls.requires:
            if not hasattr(cls, requirement):
                raise NotImplementedError(
                        f'"{cls.__name__}" must have "{requirement}".')
        super().__init_subclass__(**kwargs)
Mark Mishyn
  • 3,921
  • 2
  • 28
  • 30
0

And here is my solution:

def not_implemented_method(func):
    from functools import wraps
    from inspect import getargspec, formatargspec

    @wraps(func)
    def wrapper(self, *args, **kwargs):
        c = self.__class__.__name__
        m = func.__name__
        a = formatargspec(*getargspec(func))
        raise NotImplementedError('\'%s\' object does not implement the method \'%s%s\'' % (c, m, a))

    return wrapper


def not_implemented_property(func):
    from functools import wraps
    from inspect import getargspec, formatargspec

    @wraps(func)
    def wrapper(self, *args, **kwargs):
        c = self.__class__.__name__
        m = func.__name__
        raise NotImplementedError('\'%s\' object does not implement the property \'%s\'' % (c, m))

    return property(wrapper, wrapper, wrapper)

It can be used as

class AbstractBase(object):
    @not_implemented_method
    def test(self):
        pass

    @not_implemented_property
    def value(self):
        pass

class Implementation(AbstractBase):
    value = None

    def __init__(self):
        self.value = 42

    def test(self):
        return True
fwyzard
  • 2,364
  • 1
  • 21
  • 19
0

An interesting pattern to handle this is to set attribute to None in the parent class and to access the attribute with a function that ensure it has been set in the child class.

Here is an example from django-rest-framework:

class GenericAPIView(views.APIView):

    [...]

    serializer_class = None

    [...]

    def get_serializer_class(self):
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class
moppag
  • 956
  • 9
  • 17