Property inheritance in Python

Hi Python experts. [It should be obvious, but you can run the code in this message via python -m doctest body.txt if you saved it as body.txt] In an application I develop on I want to use properties instead of the getter/setter paradigm. I ran into a problem overriding a property in a subclass. While I know how to do it with current Python, I propose changing the behaviour to be more consistent. For presentation, here is a stupid example using getters and setters:
class BaseGetterSetter(object): ... def get_p(self): ... return self._p ... def set_p(self, value): ... self._p = value class DerivedGetterSetter(BaseGetterSetter): ... def get_p(self): ... return super(DerivedGetterSetter, self).get_p() * 2 ... def set_p(self, value): ... super(DerivedGetterSetter, self).set_p(value / 2) d = DerivedGetterSetter() d.set_p(42) d._p 21 d.get_p() 42
When translating this to use properties, I would come up with something like this:
class BaseProp(object): ... @property ... def p(self): ... return self._p ... @p.setter ... def p(self, value): ... self._p = value class DerivedProp(BaseProp): ... @property ... def p(self): ... return super(DerivedProp, self).p * 2 ... @p.setter ... def p(self, value): ... super(DerivedProp, self).p = value / 2 d = DerivedProp() d._p = 21 d.p 42 d.p = 50 Traceback (most recent call last): ... AttributeError: 'super' object has no attribute 'p'
As can be seen, using super like in the getter/setter approach above works for fget but not for fset. Support for using the getter via super() was added for Python 2.3 according to http://mail.python.org/pipermail/python-dev/2003-April/034702.html I think it would be more consistent to be able to access the __set__ call via super() as well. Working around -------------- The problematic code boils down to
super(DerivedProp, d).p = 1 Traceback (most recent call last): ... AttributeError: 'super' object has no attribute 'p'
which can be worked around like this:
BaseProp.p.fset(d, 25) BaseProp.p.__set__(d, 10)
I'd rather use the method resolution order computed by Python so that mixin classes can extend the behaviour of properties. For that, one can extend super:
class duper(super): ... def __setattr__(self, name, value): ... mro = self.__self_class__.__mro__ ... for pos in xrange(len(mro)): ... if mro[pos] == self.__thisclass__: ... break ... for pos in xrange(pos+1, len(mro)): ... tmp = mro[pos] ... if isinstance(tmp, type) and name in tmp.__dict__: ... desc = tmp.__dict__[name] ... desc.__set__(self.__self__, value) ... return duper(DerivedProp, d).p = 100 d.p 200
Extending super --------------- I wrote a test case for the super() behaviour as I would like it to be and implemented the __setattr__ of duper above into super's C implementation. The code is not of production quality and there are some open questions. But I figured that I'd rather ask for opinions before spending even more time on this. The code is available on launchpad at this URL: https://code.launchpad.net/~torsten/python/descr-set-via-super What do you think, do you agree with my understanding or am I completely wrong? Hoping for some insightful comments, Torsten
participants (1)
-
Torsten Landschoff