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?
> 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