[Python-Dev] Property inheritance in Python
Torsten Landschoff
torsten at debian.org
Sun Apr 25 17:02:55 CEST 2010
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
More information about the Python-Dev
mailing list