views:

3140

answers:

5

I have a base class with a property which (the get method) I want to overwrite in the subclass. My first thought was something like:

class Foo(object):
    def _get_age(self):
        return 11

    age = property(_get_age)


class Bar(Foo):
    def _get_age(self):
        return 44

This does not work (subclass bar.age returns 11). I found a solution with an lambda expression which works:

age = property(lambda self: self._get_age())

So is this the right solution for using properties and overwrite them in a subclass, or are there other preferred ways to do this?

+3  A: 

I agree with your solution, which seems an on-the-fly template method. This article deals with your problem and provides exactly your solution.

Federico Ramponi
+3  A: 

Yes, this is the way to do it; the property declaration executes at the time the parent class' definition is executed, which means it can only "see" the versions of the methods which exist on the parent class. So when you redefine one or more of those methods on a child class, you need to re-declare the property using the child class' version of the method(s).

James Bennett
+2  A: 

Something like this will work

class HackedProperty(object):
    def __init__(self, f):
        self.f = f
    def __get__(self, inst, owner):    
        return getattr(inst, self.f.__name__)()

class Foo(object):
    def _get_age(self):
        return 11
    age = HackedProperty(_get_age)

class Bar(Foo):
    def _get_age(self):
        return 44

print Bar().age
print Foo().age
Kozyarchuk
for the record a full implementation with set, get and del: http://infinitesque.net/articles/2005/enhancing Python's property.xhtml
Peter Hoffmann
+18  A: 

I simply prefer to repeat the property() as well as you will repeat the @classmethod decorator when overriding a class method.

While this seems very verbose, at least for Python standards, you may notice:

1) for read only properties, property can be used as a decorator:

class Foo(object):
    @property
    def age(self):
        return 11

class Bar(Foo):
    @property
    def age(self):
        return 44

2) in Python 2.6, properties grew a pair of methods setter and deleter which can be used to apply to general properties the shortcut already available for read-only ones:

class C(object):
    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

It is very easy to create similar decorators to be used with previous Python version:

class setter(object):
    def __init__(self, prop):
        self.prop = prop
    def __call__(self, setter):
        return property(
            fget=self.prop.fget,
            fset=setter,
            fdel=self.prop.fdel,
            doc=self.prop.__doc__)

class C(object):
    def __init__(self):
        self._age = None

    @property
    def age(self): 
        """My age"""
        return self._age

    @setter(age)
    def age(self, n):
        self._age = n
piro
Great answer, I wasn't aware of the added decorators in 2.6.
bouvard
+1. +2 if I could. Verbose is good. I find Python very often messy looking and confusing...
Ninefingers
+1  A: 

Another way to do it, without having to create any additional classes. I've added a set method to show what you do if you only override one of the two:

class Foo(object):
    def _get_age(self):
        return 11

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class Bar(Foo):
    def _get_age(self):
        return 44

    age = property(_get_age, Foo._set_age)

This is a pretty contrived example, but you should get the idea.

Kamil Kisiel