I'm trying to get abstract properties to work, enforcing property getter & setter definitions in downstream classes.
from abc import ABC, abstractmethod
class BaseABC(ABC):
@property
@abstractmethod
def x(self):
pass
@x.setter
@abstractmethod
def x(self, value):
pass
class MyClass(BaseABC):
def __init__(self, value):
self._x = value
@property
def x(self):
return self._x
# @x.setter
# def x(self, val):
# self._x = val
obj = MyClass(10)
print(obj.x)
obj.x = 20
print(obj.x)
Having read the documentation it seems to indicate the above should trigger a TypeError
, when the class is being build, but it only triggers an AttributeError
once the attribute is being set.
Why does the absent setter, explicitly defined in BaseABC
through an @abstractmethod
, not trigger the expected TypeError?
How does one ensure a setter is required in the daughter class?
TL;DR property
doesn't just override the getter; it overrides the setter with None
as well.
In MyClass
, property
creates a brand new property with the given getter and no setter; it doesn't simply override the getter of the inherited property. The definition of MyClass.x
is equivalent to
def x_getter(self):
return self._x
x = property(x_getter, None)
and that None
as a setter is "good enough" as far as ABC
is concerned with respect to overriding the abstract setter.
To "inherit" the abstract setter as well, use
class MyClass(BaseABC):
@BaseABC.x.getter
def x(self):
return self._x
This creates a new property not from scratch, but from the existing BaseABC.x
property, using its (abstract) setter but a new getter (just like x.setter
before created a new property using the old getter but a new setter).
To make MyClass
instantiable, you still need to provide a concrete setter using x.setter
.
Unfortunately, nothing forces you to to use BaseABC.x.getter
in place of property
. ABC
only cares that x
gets set to something appropriate. This is a general problem with ABC
, not properties in particular. There is only so much you can do at the library level; abstract base classes are not a feature of Python itself.