What I'd really like to do is something like this:
class X(metaclass=abc.ABCMeta):
@abc.abstractAttribute # this doesn't exist
var = [1,2]
class Y(X):
var = X.var + [3,4]
This would force any subclasses of X to implement a static var attribute.
But there's no way to define a static attribute as abstract.
Attempting to use @abc.abstractmethod, combined with property, I can get close:
class X(metaclass=abc.ABCMeta):
@property
@abc.abstractmethod
def var(self):
return [1,2]
class Y(X):
@property
def var(self):
return super().var + [3,4]
y=Y(); y.var gives [1,2,3,4] as desired.
But the goal is to create a static attribute/property, not an instance attribute/property.
When creating as a @staticmethod:
class X(metaclass=abc.ABCMeta):
@property
@staticmethod
@abc.abstractmethod
def var():
return [1,2]
class Y(X):
@property
@staticmethod
def var():
return X.var + [3,4]
... then y=Y(); y.var gives TypeError 'staticmethod' object is not callable
If I switch the decorator order, this will resolve the staticmethod callable error:
class X(metaclass=abc.ABCMeta):
@staticmethod
@property
@abc.abstractmethod
def var():
return [1,2]
class Y(X):
@staticmethod
@property
def var():
return X.var + [3,4]
The static method will work, but the property will not function as intended: y=Y(); y.var returns the property itself, <property at 0x2c16aa1b3b8>.
This answer is close to what's desired, but it doesn't work in this case since the base class, X, will already have a var attribute (so subclass can access via super).
How do you define a python class to have an abstract, static attribute/property?
If the requirement that it be a property is flexible, you could just define a static abstract method:
class X:
@staticmethod
@abstractmethod
def var():
pass
class Y(X):
@staticmethod
def var():
return [1, 2]
>>> Y.var()
[1, 2]
Maybe a variation on your solution could work - instead of using dict simply testing whether parent's and subclass static variables are the same object. It works for grandchildren and slots, but the tradeoff is that it will throw the exception if you override None with None, or other object with the same object, so it's not perfect.
class X:
var = [1,2]
def __init_subclass__(cls):
if X.var is cls.var:
raise NotImplementedError(
"Attribute '{}' has not been overriden in class '{}'" \
.format('var', cls.__name__)
)
Maybe I'm overlooking something crucial, but it worked in simple use cases.
Let me know if you're aware of a more traditional way to solve this that will also work for classes that use __slots__. But for my use case, the below will work:
class X:
var = [1,2]
def __init_subclass__(cls):
attr_name = 'var'
if not attr_name in cls.__dict__:
raise NotImplementedError(
"Attribute '{}' has not been overriden in class '{}'" \
.format(attr_name, cls.__name__)
)
class Y(X):
# var = X.var + [3,4] # this will work
pass # this won't work
y = Y()
edit: There is another downside to using this approach, which is that this will wrongly error for grandchildren who do not override:
class Y(X):
var = X.var + [3,4]
y = Y() # this works
class Z(Y):
pass
z = Z() # this errors
I will leave this answer as unmarked for someone to suggest a better alternative.