I have two classes A
and B
, where B
inherits from A
and overrides a property. A
is not under my control so I cannot change it.
The code looks as follows:
class A():
def __init__(self, value):
self.value = value
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value
class B(A):
def __init__(self, value):
super(B, self).__init__(value)
@property
def value(self):
return super(B, self).value
@value.setter
def value(self, value):
raise AttributeError("can't set attribute")
When I try to call B(1)
I obviously get AttributeError: can't set attribute
.
I would like to have a different behaviour when value
is set from inside class methods
@value.setter
def value(self, value):
if set from inside class hierarchy:
pass
else:
raise AttributeError("can't set attribute")
The module inspect
does not seem to give me enough information to do this, except checking against a list of known functions.
You can inspect the stack to determine who called, and whether that it's in the class hierarchy to decide whether or not to allow it:
import inspect
def who_called():
frame = inspect.stack()[2][0]
if 'self' not in frame.f_locals:
return None, None
cls = frame.f_locals['self'].__class__
method = frame.f_code.co_name
return cls, method
class A(object):
def __init__(self, value):
self._value = value
@property
def value(self):
return self._value
@value.setter
def value(self, value):
self._value = value
# Assuming this existed it would also work
def change_value(self, value):
self.value = value
Class B
now checking:
class B(A):
def __init__(self, value):
super(B, self).__init__(value)
@property
def value(self):
return super(B, self).value
@value.setter
def value(self, value):
cls, method = who_called()
if cls in B.__mro__ and method in A.__dict__:
self._value = value
else:
raise AttributeError("can't set attribute")
Proof:
b = B('does not raise error')
b.change_value('does not raise error')
b.value = 'raises error'