Joel Cornett
Joel Cornett

Reputation: 24788

Enforcing Method Definition for a Class

I've written the following code to help enforce the definition of certain methods for a class:

def createClassTemplate(name, requiredMethods=[], inherits=object):
    def require(name):
        def errorRaiser(self, *args, **kwargs):
            raise RequireException("method '{}' must be defined.".format(name))
        setattr(wrapper, "__name__", name)
        return errorRaiser

    class Custom(inherits): pass

    setattr(Custom, "__name__", name)
    for methodName in requiredMethods:
        setattr(Custom, methodName, require(methodName))

    return Custom

Which is implemented like this:

Model = createClassTemplate("Model", ["foo", "bar", "baz"])

class MyClass(Model):
    pass

This way, when I'm missing a method, the calling class will generate a meaningful error indicating that I've failed to define a required method.

The problem is, the above code seems uncomfortably hacky and unpythonic. Am I going about this right way? Should I even be forcing class templates like this? And is there a better way to accomplish the same thing?

Upvotes: 1

Views: 88

Answers (4)

Martijn Pieters
Martijn Pieters

Reputation: 1125248

You should use a metaclass instead.

The standard library comes with a ready-made metaclass for just this task, the ABCMeta metaclass:

import abc

class Model(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def foo(self):
        pass

    @abc.abstractmethod
    def bar(self):
        pass

    @abc.abstractmethod
    def baz(self):
        pass

class MyClass(Model):
    pass

Demonstration:

>>> import abc
>>> class Model(object):
...     __metaclass__ = abc.ABCMeta
...     @abc.abstractmethod
...     def foo(self):
...         pass
...     @abc.abstractmethod
...     def bar(self):
...         pass
...     @abc.abstractmethod
...     def baz(self):
...         pass
... 
>>> class MyClass(Model):
...     pass
... 
>>> myclass = MyClass()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods bar, baz, foo

Once MyClass does provide implementations of the abstract methods, instantiation succeeds:

>>> class MyClass(Model):
...     def foo(self): pass
...     def bar(self): pass
...     def baz(self): pass
... 
>>> myclass = MyClass()

Upvotes: 2

Wolph
Wolph

Reputation: 80111

It seems to me that you are trying to reinvent the Abstract Base Classes

I think you want something like this:

import abc

class MyBaseClass:
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def foo(self):
        pass

    @abc.abstractmethod
    def bar(self):
        pass

    @abc.abstractmethod
    def baz(self):
        pas

Upvotes: 0

Jon Clements
Jon Clements

Reputation: 142256

If you're using Python 2.6+, there's the concept of an ABC - http://docs.python.org/2/library/abc.html

Upvotes: 0

ecatmur
ecatmur

Reputation: 157504

You can use the ABCMeta metaclass:

from abc import ABCMeta, abstractmethod

class MyClass:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

Subclasses of MyClass must override foo to be allowed to be instantiated.

Upvotes: 4

Related Questions