triccare
triccare

Reputation: 145

Define an abstract method but require a subclass implementation

Note I do not think that abc inherently solves what I'm looking for. Restating in another, maybe better way, I'm looking of a way to partially implement a Parent.method but require that there is a Subclass.method also, which uses and adds to the partial Parent.method implementation.

I would like to partially define an abstract class method, but still require that the method be also implemented in a subclass. For example:

class AbstractClass(object):
    def amethod():
        # some code that should always be executed here
        vars = dosomething()

        # But, since we're the "abstract" class
        # force implementation through subclassing
        if <somehow determine whether this has not been subclassed>:
            raise NotImplementedError

class ActualClass(AbstractClass):
    def amethod():
        # Actual class code
        code_here()

        # And execute the super class code.
        super(ActualClass, self).amethod()

Upvotes: 3

Views: 5182

Answers (5)

janluke
janluke

Reputation: 1677

I use to call this "fill-in-the-blank pattern" (NB: this is not a design pattern). You define a concrete method in the abstract class which calls abstract methods and works as a template with "blanks" (the abstract methods). The sub-classes "fill the blanks" implementing the abstract methods. In your simple case:

class AbstractClass(object):
    def amethod(self):
        self._amethod()
        print('Common operations')

    @abc.abstractmethod
    def _amethod(self, vars):
        pass


class ConcreteClass(AbstractClass):
    def _amethod(self):
        print('Concrete class code')

Usually you can give a better name to the abstract method.

Upvotes: 0

LemonPi
LemonPi

Reputation: 1156

Note I do not think that abc inherently solves what I'm looking for.

Actually abc is exactly what you're looking for. Defining an implementation in the base class but decorating it as abstract requires deriving classes to redefine it. Of course this has the side effect of preventing you from instantiating the base class, which I assume is OK in your use case.

import abc


# inheritance from abc.ABC is important, as otherwise the decorators don't do anything
class AbstractClass(abc.ABC):
    @abc.abstractmethod
    def amethod(self):
        # some code that should always be executed here
        print("Base implementation")


class ActualClass(AbstractClass):
    # will return TypeError: Can't instantiate abstract class ActualClass with abstract methods amethod if not redefined
    def amethod(self):
        # Actual class code
        print("Actual implementation")

        # And execute the super class code. (only one super class so less confusing)
        super().amethod()


a = ActualClass()
a.amethod()

Upvotes: 4

demented hedgehog
demented hedgehog

Reputation: 7548

Also this is interesting

def abstractmethod(method):
    def default_abstract_method(*args, **kwargs):
        raise NotImplementedError('call to abstract method ' + repr(method))    
    default_abstract_method.__name__ = method.__name__        
    return default_abstract_method

http://code.activestate.com/recipes/577666-abstract-method-decorator/

Though I haven't used it.

Upvotes: 1

demented hedgehog
demented hedgehog

Reputation: 7548

Test like this?

class AbstractClass(object):
    def amethod(self):
        # some code that should always be executed here
        print(" AbstractClass.amethod()")

        # But, since we're the "abstract" class
        # force implementation through subclassing
        if self.__class__ == AbstractClass:
            raise NotImplementedError

class ActualClass(AbstractClass):
    def amethod(self):
        # Actual class code
        print(" ActualClass.amethod()")

        # And execute the super class code.
        super(ActualClass, self).amethod()


#a = AbstractClass()
#a.amethod()

b = ActualClass()
b.amethod()

Upvotes: 1

Zizouz212
Zizouz212

Reputation: 4998

You could force it by raising an exception in the parent:

class base(object):
    def a_method(self):
        raise NotImplementedError("Implement this!")

class second(base):
    pass

I would get an exception if I call second().a_method(). There's no such thing as abstract in Python, but this is likely your best way of doing it. Otherwise,

import abc
class base(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def something_to_implement(this):
        """A docstring"""
        return

This will make the method "abstract" by attempting to raise a TypeError if initialized.

Upvotes: 0

Related Questions