Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create an abstract class attribute (potentially read-only)

I have spent a lot of time researching this, but none of the answers seem to work how I would like.

I have an abstract class with a class attribute I want each subclass to be forced to implement

class AbstractFoo():
    forceThis = 0

So that when I do this

class RealFoo(AbstractFoo):
    pass

it throws an error telling me it can't create the class until I implement forceThis.

How can I do that?

(I don't want the attribute to be read-only, but if that's the only solution, I'll accept it.)

For a class method, I've discovered I can do

from abc import ABCMeta, abstractmethod

class AbstractFoo(metaclass=ABCMeta):
    @classmethod
    @abstractmethod
    def forceThis():
        """This must be implemented"""

so that

class RealFoo(AbstractFoo):
    pass

at least throws the error TypeError: Can't instantiate abstract class EZ with abstract methods forceThis

(Although it doesn't force forceThis to be a class method.)

How can I get a similar error to pop up for the class attribute?

like image 589
Pro Q Avatar asked May 04 '17 18:05

Pro Q


1 Answers

You can do this by defining your own metaclass. Something like:

 class ForceMeta(type):
     required = ['foo', 'bar']

     def __new__(mcls, name, bases, namespace):
         cls = super().__new__(mcls, name, bases, namespace)
         for prop in mcls.required:
            if not hasattr(cls, prop):
               raise NotImplementedError('must define {}'.format(prop))
         return cls

Now you can use this as the metaclass of your own classes:

class RequiredClass(metaclass=ForceMeta):
     foo = 1

which will raise the error 'must define bar'.

like image 172
Daniel Roseman Avatar answered Sep 29 '22 08:09

Daniel Roseman