class Controller(object):
def __new__(cls, *args, **kargs):
if not hasattr(cls,'path'):
raise NotImplementedError("'Controller' subclasses should have a 'path' attribute")
return object.__new__(cls)
class C1(Controller):
path = 42
class C2(Controller):
pass
c1 = C1()
# ok
c2 = C2()
# NotImplementedError: 'Controller' subclasses should have a 'path' attribute
In [20]: class X:
...: def __init_subclass__(cls):
...: if not hasattr(cls, 'required'):
...: raise NotImplementedError
In [21]: class Y(X):
...: required = 5
...:
In [22]: Y()
Out[22]: <__main__.Y at 0x7f08408c9a20>
from abc import ABC
class A(ABC):
@classmethod
def __init_subclass__(cls):
required_class_variables = [
'foo',
'bar',
]
for var in required_class_variables:
if not hasattr(cls, var):
raise NotImplementedError(
f'Class {cls} lacks required `{var}` class attribute'
)
from abc import ABC, abstractmethod
class BaseController(ABC):
@property
@abstractmethod
def path(self) -> str:
...
class Controller(BaseController):
path = "/home"
# Instead of an elipsis, you can add a docstring for clarity
class AnotherBaseController(ABC):
@property
@abstractmethod
def path(self) -> str:
"""
:return: the url path of this controller
"""