[Python-Dev] Super and properties (original) (raw)

Guido van Rossum guido@python.org
Wed, 16 Apr 2003 15:31:48 -0400


(I'm quoting the whole message below since this has been two weeks by now.)

From: =?iso-8859-1?Q?Gon=E7aloRodrigues?= <op73418@mail.telepac.pt>

Hi all, Since this is my first post here, let me first introduce myself. I'm Gon�alo Rodrigues. I work in mathematics, mathematical physics to be more precise. I am a self-taught hobbyist programmer and fell in love with Python a year and half ago. And of interesting personal details this is about all so let me get down to business. My problem has to do with super that does not seem to work well with properties. I posted to comp.lang.python a while ago and there I was advised to post here. So, suppose I override a property in a subclass, e.g. >>> class test(object): ... def init(self, n): _... self.n = n _... def getn(self): _... return self.n _... def setn(self, n): _... self.n = n _... n = property(_getn, setn) ... >>> a = test(8) >>> a.n 8 >>> class test2(test): ... def init(self, n): ... super(test2, self).init(n) _... def getn(self): ... return "Got ya!" _... n = property(getn) ... >>> b = test2(8) >>> b.n 'Got ya!' Now, since I'm overriding a property, it is only normal that I may want to call the property implementation in the super class. But the obvious way (to me at least) does not work: >>> print super(test2, b).n Traceback (most recent call last): File "", line 1, in ? AttributeError: 'super' object has no attribute 'n' I know I can get at the property via the class, e.g. do >>> test.n.get(b) 8 >>> Or, not hardcoding the test class, >>> b.class.mro[1].n.get(b) 8 But this is ugly at best. To add to the puzzle, the following works, albeit not in the way I expected >>> super(test2, b).getattribute('n') 'Got ya!' Since I do not know if this is a bug in super or a feature request for it, I thought I'd better post here and leave it to your consideration. With my best regards, G. Rodrigues

Hah! I think I've resolved this, and I still don't know if it's a bug report or a feature request. :-)

The crux of the matter is that super() has a specific exception for data descriptors, of which properties are an example. This means that when looking for attribute 'x', if it finds a hit which is a data descriptor, it ignores it and keeps looking.

It took me a while to understand why. When I disabled the test, exactly one unit test fails, and it wasn't immediately clear what was going on.

It turns out that this test was asking for the class attribute of the super object itself, but it was getting the class of the instance. Simplified:

class C(object): pass print super(C, C()).class

This should print <type 'super'> and not <class '__main__.C'>, because it would be really confusing if the super object, when inquired about its class, masqueraded as another class.

How does skipping data descriptors accomplish this goal? When super does its search, the last class it looks at is 'object', at the end of the MRO chain. And this has a data descriptor for 'class', which describes the class attribute of all objects. If super were to give this descriptor the usual treatment, it would call its get method, and that would (in the above example) return the class C.

The CVS history mentions (for typeobject.c rev 2.120, shortly before the final 2.2.0 release):

super(C, C()).class would return the class attribute of C() rather than the class attribute of the super object. This is confusing. To fix this, I decided to change the semantics of super so that it only applies to code attributes, not to data attributes. After all, overriding data attributes is not supported anyway.

Your message above makes a good case for overriding data attributes, so I have to retract this. But I don't want class to return C, I want it to return super. So I'll change this back, and make an explicit exception only for class.

And ok, I'm deciding now that this is a feature, which means that I'm changing it in Python 2.3, but not backporting the change to Python 2.2.x.

Hope this helps!

--Guido van Rossum (home page: http://www.python.org/~guido/)