TAGS :Viewed: 9 - Published at: a few seconds ago

[ @classmethod with Abstract Base Class ]

I have an Abstract Base Class and subclasses defined as follows (Python 2.7):

import abc
import MyDatabaseModule

class _DbObject(object):
    __metaclass__ = abc.ABCMeta

    def _GetObjectType(self):
        raise NotImplementedError, "Please override in the derived class"

    ObjectType = abc.abstractproperty(_GetObjectType, None)

class Entry(_DbObject):
    _objectTypeID = 'ENTRY'

    def _GetObjectType(self):
        return MyDatabaseModule.DoesSomethingWith(self._objectTypeID)

    ObjectType = property(_GetObjectType, None)

This works fine, meaning that the base class _DbObject cannot be instantiated because it has only an abstract version of the property getter method.

try:
    dbObject = _DbObject()
    print "dbObject.ObjectType: " + dbObject.ObjectType
except Exception, err:
    print 'ERROR:', str(err) 

Now I can do:

entry = Entry()
print entry.ObjectType

to get access to the ObjectType property. However, what I would like to be able to do is just:

print Entry.ObjectType

However, wherever I try to insert @classmethod, I get the error classmethod object is not callabale.

Answer 1


The problem is not your ABC but the simple fact, that there is no such thing as a classproperty in python, you have to create it on your own. Actually there is a good question + answer on SO about that. It actually should be no problem using it with your ABC aswell.

Answer 2


So, the magic for the way "property" works in Python is implemented using the descriptor protocol - property itself if a powerful built-in that provides a descriptor that works well for instances, not classes as you had seen.

So, you need a "class property" - the property built-in can't give you that, but the descriptor protocol can. What the descriptor protocol says is that whenever an attribute is retrieved from the class, if it is an object with a __get__ method, that method is called with "self, instance, owner" - if it is retrieved from the class, instead of from an instance, you the "instance" parameter is None -

Btw, as stated by @Constantinius, this does not have to do with the ABC's at all, just with you wanting a "class property".

class classproperty(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, instance, owner):
        return self.func(owner)


class Entry(_DbObject):
    _objectTypeID = 'ENTRY'

    def _GetObjectType(cls):
        return MyDatabaseModule.DoesSomethingWith(cls._objectTypeID)
    ObjectType = classproperty(_GetObjectType, None)