3

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()
triccare
  • 145
  • 1
  • 9
  • 3
    Why do you need more classes that don't actually do anything? – TigerhawkT3 Jun 05 '15 at 01:45
  • possible duplicate of [Abstract methods in Python](http://stackoverflow.com/questions/4382945/abstract-methods-in-python) – Zizouz212 Jun 05 '15 at 01:53
  • @TigerhawkT3 This is why I want to do the above. I do want it to do something, but that something should occur for every subclass. It is just that without the subclass, the partial is meaningless. Part of asking this is to be informed that "Yes, this is bad, do what you want this way..." So far however, this hasn't happened, yet. – triccare Jun 05 '15 at 19:38
  • It's not that it's bad (i.e., producing errors or data loss), it's that it's probably unnecessary and largely unenforceable. What if someone comes along and makes a subclass that's nothing more than a wrapper around the superclass? – TigerhawkT3 Jun 05 '15 at 19:47
  • Though my particular case this is not an issue, this method definitely fails in enforcement against the determined. For that, I presume the only real solution is calling through an API where the API would enforce the extra behavior. Though some method of metaprogramming could be done also. – triccare Jun 05 '15 at 20:53

5 Answers5

2

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()
LemonPi
  • 1,026
  • 9
  • 22
1

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()
demented hedgehog
  • 7,007
  • 4
  • 42
  • 49
  • I'm assuming you want self in the function arg list or are these going to be static methods or something else? – demented hedgehog Jun 05 '15 at 01:53
  • Well this would have to be inherited and would have to be in `__init__` – Zizouz212 Jun 05 '15 at 01:53
  • See if that does what you want. (Just changed it top make it work in python 3 as well). – demented hedgehog Jun 05 '15 at 01:56
  • This would implement the concept as I was thinking it. Keep forgetting about __class__. And for my purposes would be sufficient. – triccare Jun 05 '15 at 19:20
  • 1
    This answer does not force the method to be implemented in a subclass. If the user does not implement amethod in ActualClass, the check will still pass because b.__class__ will be ActualClass, and not AbstractClass. – zoidberg Jan 11 '17 at 18:58
1

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.

demented hedgehog
  • 7,007
  • 4
  • 42
  • 49
  • yep.. Nice thing about is is it moves the complexity out of the abstract class. – demented hedgehog Jun 05 '15 at 02:13
  • Yes, this is interesting, and I did actually start this approach while waiting for discussion here. However, I'm voting on the other answer because fitting into the class definition, which has bunches of other stuff, keeps the concept implementation "closer" to where it is actually needed. – triccare Jun 05 '15 at 19:23
0

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.

Zizouz212
  • 4,908
  • 5
  • 42
  • 66
0

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.

janluke
  • 1,567
  • 1
  • 15
  • 19