Reputation: 2844
I have a SuperClass which defines a property and it's setter, like so:
class A(object):
def __init__(self):
self._mode = None
@property
def mode(self):
# to be overriden in subclass to implement the actual getter code
raise NotImplementedError
@mode.setter
def mode(self, value):
# common assertions and input validations
self._set_mode(value)
def _set_mode(self, value):
# to be overriden in subclass to implement the actual setter code
raise NotImplementedError
class B(A):
@property
def mode(self):
return self._mode
def _set_mode(self, value):
self._mode = value
obj = B()
obj.mode = 'test'
Which raises
obj.mode = 'test'
AttributeError: can't set attribute
It would seem that I have to register a setter in B. I'd usually do this like @A.mode.setter
, but that doesn't quite apply here as I don't actually want to define a new setter in B, just re-use the one from A.
Does anyone have a hint on how to solve this? Might be trivial, but I'm not seeing it right now :/
Upvotes: 7
Views: 4761
Reputation: 251
A workaround to the cpython bug is to fetch the setter from the parent class manually
ParentClass.some_property.fset(self, value)
Example:
>>> class A:
... @property
... def mode(self):
... return 2
...
... @mode.setter
... def mode(self, value):
... print("A", value)
...
>>> class B(A):
... @property
... def mode(self):
... return super().mode + 1
...
... @mode.setter
... def mode(self, value):
... print("B", value)
... A.mode.fset(self, value + 1)
...
>>> a = A()
>>> b = B()
>>> print(f"{a.mode = }")
a.mode = 2
>>> print(f"{b.mode = }")
b.mode = 3
>>> a.mode = 5
A 5
>>> b.mode = 5
B 5
A 6
If you want it parent-agnostic, then look through self.__class__.__bases__
.
Upvotes: 0
Reputation: 629
It works for me this way
class A:
@property
def mode(self):
raise NotImplemented
@mode.setter
def mode(self, value):
raise NotImplemented
class B(A):
@A.mode.setter
def mode(self, value):
# your setter implement here
Upvotes: 1
Reputation: 128
Analogously with using mode.setter
inside A
’s definition, this answer to a related question suggests to use a property of a base class to define a property on a subclass like so:
class B(A):
@A.mode.getter # only this line is changed!
def mode(self):
return self._mode
def _set_mode(self, value):
self._mode = value
Here, mode.setter
will be the same as it was for A
, but we’ve replaced the getter.
Upvotes: 3
Reputation: 77912
the getter and setter are stored as attributes of the property
object (respectively as .fget
and .fset
), so as soon as you overload the property in a child class you most explicitely provide both getter and setters, ie:
class B(A):
@property
def mode(self):
return self._mode
@mode.setter
def mode(self, value):
self._mode = value
So if you want to make the getter and/or setter overloadable without having to redeclare the property, you have to define a _get_mode
method and make your property's getter delegate to this method, just like you did for the setter.
class A(object):
def __init__(self):
self._mode = None
@property
def mode(self):
return self._get_mode()
def _get_mode(self):
# to be overriden in subclass to implement the actual getter code
raise NotImplementedError
@mode.setter
def mode(self, value):
# common assertions and input validations
self._set_mode(value)
def _set_mode(self, value):
# to be overriden in subclass to implement the actual setter code
raise NotImplementedError
class B(A):
def _get_mode(self):
return self._mode
def _set_mode(self, value):
self._mode = value
Upvotes: 7