views:

520

answers:

3

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.

+1  A: 

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
This is simpler, but I like how my version throws immediately and not only if the attribute happens to be used.
Glenn Maynard
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
Ah, this is what I was after.
Kiv
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
Goofy comment system strikes again. I'm sure you can figure out what I meant. :|
Glenn Maynard
@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 Zaslavsky
+1  A: 
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
+3  A: 

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
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
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