Reputation: 1745
Considering the following basic Python classes:
class Foo(object):
def __init__(self, a1):
self._att1 = a1
@property
def att1(self):
return self._att1
@att1.setter
def att1(self, a):
try:
self._att1 = a
except TypeError:
# handle exception
print("Not allowed")
class Bar(Foo):
def __init__(self):
Foo.__init__(self, 100)
@Foo.att1.setter
def att1(self, a):
# self.att1 = a * 0.5 # RecursionError: maximum recursion depth exceeded
# Foo.att1 = a * 0.5 # attribute is not changed (base instances are though (?))
# Foo.att1(self,a) # TypeError: 'property' object is not callable
super().att1.__set__(a * 0.5) # AttributeError: 'int' object has no attribute '__set__'
# ... some other additional code ...
a = Foo(5)
b = Bar()
b.att1 = 700
print(a.att1)
print(b.att1)
What would be syntax for calling base property setter from within its override on the child class? I understand I could set self._att1
directly, but I would like to avoid that because I would then need to repeat exception handling code. This but a simple example, and I have more complex cases where the base class implements some additional operations on the attribute, and I would like to avoid repeating the same code on the derived class property setter.
Upvotes: 1
Views: 71
Reputation: 7812
Code:
class Foo:
def __init__(self, a1):
self._att1 = a1
@property
def att1(self):
return self._att1
@att1.setter
def att1(self, a):
if not isinstance(a, int):
print("Not allowed")
else:
self._att1 = a
class Bar(Foo):
def __init__(self):
Foo.__init__(self, 100)
@Foo.att1.setter
def att1(self, a):
Foo.att1.fset(self, a * 2)
c = Bar()
print(c.att1)
c.att1 = 10
print(c.att1)
c.att1 = "some string"
Output:
100
20
Not allowed
UPD.
Following @chepner advice, I've decided to add some explanation.
When you're using decorator @Foo.att1.setter
it doesn't work like you expected.
In docs you can see 2 examples of declaring properties: assign class variable using property()
function and use @property
decorator. Both this methods are equialent, but I find first much more obvious in case of demonstration how does provided code works.
Let's rewrite class declarations using property()
function instead decorators:
class Foo:
def __init__(self, a1):
self._att1 = a1
def getatt1(self):
return self._att1
def setatt1(self, a):
if not isinstance(a, int):
print("Not allowed")
else:
self._att1 = a
att1 = property(getatt1, setatt1)
class Bar(Foo):
def __init__(self):
super().__init__(100)
def setatt1(self, a):
Foo.att1.fset(self, a * 2)
att1 = property(Foo.getatt1, setatt1)
As you see, you don't override property, you're creating new with same name, which shadow base class property. You can prove it using this code:
print(f"Foo.att1 is Bar.att1: {Foo.att1 is Bar.att1}")
In both declarations it'll return False
, which means that property objects of this classes aren't same.
Upvotes: 3