callable virtual method

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri Aug 14 17:56:27 CEST 2009


On Fri, 14 Aug 2009 16:49:47 +0200, Jean-Michel Pichavant wrote:

> Hi fellows,
> 
> Does anyone know a way to write virtual methods (in one virtual class)
> that will raise an exception only if called without being overridden ?
> Currently in the virtual method I'm checking that the class of the
> instance calling the method has defined that method as well.

I'm not entirely sure of the terminology -- is this the same as an 
abstract base class? Googling has not enlightened me. Given your example, 
it seems to be.


> Example:
> 
> class Stream(object):
>     """Interface of all stream objects"""
>     def resetStats(self):
>         """Reset the stream statistics. All values a zeroed except the
> date."""
>         _log.info('Reset statistics of %s' % self)
>         if self.__class__.resetStats == Stream.resetStats:
>             raise NotImplementedError()

The usual idiom I've seen for abstract methods is to simplify the check, 
and to put it *before* any work is done:

class Stream(object):
    """Interface of all stream objects"""
    def resetStats(self):
        if self.__class__ is Stream:
            raise NotImplementedError()
        _log.info('Reset statistics of %s' % self)

Even simpler is to just put the check in __init__, so to prevent the 
caller from creating an instance of the class:

class AbstractStream(object):
    def __init__(self):
        if self.__class__ is Stream:
            raise NotImplementedError('abstract class')
    def resetStats(self):
        # This does not need to be over-ridden.
        _log.info('Reset statistics of %s' % self)
    def whatever(self):
        # This *must* be over-ridden, and *cannot* be called
        raise NotImplementedError('abstract method')


If you have a lot of methods, you can probably reduce the boilerplate 
with decorators:


# Untested
from functools import wraps
def abstract(func):
    # Abstract methods don't have to be over-ridden, so long as they
    # are called from a subclass of the abstract class.
    @functools.wraps(func)
    def inner(self, *args, **kwargs):
        if self.__class__ is Stream:
            raise NotImplementedError()
        return func(self, *args, **kwargs)
    return inner

def virtual(func):
    # Virtual methods must be over-ridden, and must not be called by
    # inheritance.
    @functools.wraps(func)
    def inner(self, *args, **kwargs):
        raise NotImplementedError()
    return inner

class Stream(object):
    @abstract
    def __init__(self):
        pass
    def resetStats(self):
        _log.info('Reset statistics of %s' % self)
    @virtual
    def whatever(self):
        pass



-- 
Steven



More information about the Python-list mailing list