Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

properties and inheritance

6 views
Skip to first unread message

Neal D. Becker

unread,
Oct 7, 2004, 7:34:32 AM10/7/04
to pytho...@python.org
It seems that properties, as implemented in 2.3, don't behave as I'd expect
with respect to inheritance:

class A (object):
def f(self):
print "A"

doF = property (fget=f)

class B(A):
def f(self):
print "B"


b = B()
b.doF : prints "A"

Is there a rationale for this? I would have thought that if properties are
a shorthand for calling functions, and functions are polymorphic, then why
are properties not? Doesn't that make properties behaviour inconsistent
with other attributes?

Alex Martelli

unread,
Oct 7, 2004, 7:57:00 AM10/7/04
to
Neal D. Becker <ndbe...@verizon.net> wrote:

> It seems that properties, as implemented in 2.3, don't behave as I'd expect
> with respect to inheritance:
>
> class A (object):
> def f(self):
> print "A"
>
> doF = property (fget=f)
>
> class B(A):
> def f(self):
> print "B"
>
> b = B()
> b.doF : prints "A"
>
> Is there a rationale for this?

Object 'doF' (of type 'property', held in A.__dict__) holds as its
'fget' attribute just what you passed to it -- the 'f' function that's
held in A.__dict__. IOW,
A.doF.fget is A.__dict__['f']

> I would have thought that if properties are
> a shorthand for calling functions,

They are -- that A.doF.fget is indeed a function, and the __get__ method
of the property calls the function in question. *The function*, not any
other function of the same name that might happen to be elsewhere...

> and functions are polymorphic, then why
> are properties not?

Functions are descriptors and so are properties -- both have __get__
methods. It's not an issue of polymorphism: it's an issue of early vs
late binding. What function a property holds (and thus calls) is bound
pretty early, when the property object is created; what attribute an
access such as b.f would get (and there is no such access going on here)
is bound very late, at the time the access is executed.

> Doesn't that make properties behaviour inconsistent
> with other attributes?

Not particularly. B subclasses A, A has an attribute named 'doF' which
B doesn't override (nor does instance b of B), so the access b.doF uses
A.doF.__get__(b, B). Since object A.doF is holding a function object
(not a name -- it doesn't do any getattr, therefore), it call the
function object it holds, not another. You'd have exactly the same
effect going on if you coded, say:

class Foo:
def bar(self): return 'Foo'
def callbar(self, bar=bar): return bar(self)

class Bar(Foo):
def bar(self): return 'Bar'

b = Bar()
print b.callbar()

No inconsistency between behavior of function callbar and behavior of a
property with bar as its fget -- both bind early, when coded this way.

If you want more indirectness it's not hard to obtain it, either by
having the property hold a function that does some further attribute
lookup, or by using instead of property some custom descriptor type,
call it indirect_property, which holds a name and looks it up. The
extra lookup at each access will of course make things slower, but if
you do need the indirection then that may be acceptable. I consider it
a reasonable design decision to make the leaner, faster descriptor the
built-in one, personally.


Alex

0 new messages