63

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?

Peter Hoffmann
  • 56,376
  • 15
  • 76
  • 59

10 Answers10

72

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
Neil G
  • 32,138
  • 39
  • 156
  • 257
piro
  • 13,378
  • 5
  • 34
  • 38
  • 5
    Wait, so the proposed solution to getting behaviour defined in a parent class is to re-implement it? Doesn't that seem like a blatant violation of DRY (don't repeat yourself)? – Adam Parkin Jun 26 '12 at 19:54
  • 2
    This fails in the case where you only want to alter the behavior of either the getter or setter but not both. If you alter only the getter, you will get an AttributeError when trying to use the setter. Altering just the setter pretty much just doesn't work. The only way that defining it works (assuming the getter is not redefined) is if you use @..setter however this is incorrect as it will modify the property of the superclass and not of the subclass which is definitely not what you want. – Matt Jan 02 '13 at 19:54
  • Not true, I've provided a case below where only the setter is altered (although either getter, setter, or both could be altered if desired). – Mr. B Jul 01 '14 at 18:01
21

I don't agree that the chosen answer is the ideal way to allow for overriding the property methods. If you expect the getters and setters to be overridden, then you can use lambda to provide access to self, with something like lambda self: self.<property func>.

This works (at least) for Python versions 2.4 to 3.6.

If anyone knows a way to do this with by using property as a decorator instead of as a direct property() call, I'd like to hear it!

Example:

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    meow = property(fget=lambda self: self._get_meow(),
                    fset=lambda self, value: self._set_meow(value))

This way, an override can be easily performed:

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'

so that:

>>> foo = Foo()
>>> bar = Bar()
>>> foo.meow, bar.meow = "meow", "meow"
>>> foo.meow
"meow from a Foo"
>>> bar.meow
"meow from a Foo, altered by a Bar"

I discovered this on geek at play.

Mr. B
  • 2,536
  • 1
  • 26
  • 26
12

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
  • 19,723
  • 11
  • 46
  • 56
  • This seems to be a correct solution if python < 2.6 is needed. – ziima Jun 18 '14 at 13:27
  • This is a decent enough solution which works with python 2.4+, but I like the solution I found on _geek at play_ better, as it doesn't require re-creating the property in the subclass -- just the getter or setter method can be overridden, and normal MRO applies. – Mr. B Jul 01 '14 at 18:08
  • 1
    I use this approach too. I found this code easier to read and subclasses have access to super().value_getter() or setter function. – VPfB Dec 21 '15 at 09:03
7

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
  • 10,903
  • 4
  • 35
  • 24
6

A possible workaround might look like:

class Foo:
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        print('Foo: getting age')
        return self._age

    @age.setter
    def age(self, value):
        print('Foo: setting age')
        self._age = value


class Bar(Foo):
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        return super().age

    @age.setter
    def age(self, value):
        super(Bar, Bar).age.__set__(self, value)

if __name__ == '__main__':
    f = Foo(11)
    print(f.age)
    b = Bar(44)
    print(b.age)

It prints

Foo: setting age
Foo: getting age
11
Foo: setting age
Foo: getting age
44

Got the idea from "Python Cookbook" by David Beazley & Brian K. Jones. Using Python 3.5.3 on Debian GNU/Linux 9.11 (stretch)

2

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
  • 21,049
  • 14
  • 40
  • 46
  • for the record a full implementation with set, get and del: http://infinitesque.net/articles/2005/enhancing Python's property.xhtml – Peter Hoffmann Oct 26 '08 at 04:00
  • FWIW, here's a currently working link to the article [An Overrideable Alternative to the property Function in Python](http://infinitesque.net/articles/2005/enhancing%20Python%27s%20property.xhtml) mentioned in Peter Hoffmann's comment. – martineau Feb 28 '13 at 01:24
2

Same as @mr-b's but with decorator.

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    @property
    def meow(self):
        return self._get_meow()
    @meow.setter
    def meow(self, value):
        self._set_meow(value)

This way, an override can be easily performed:

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'
Community
  • 1
  • 1
Nizam Mohamed
  • 8,751
  • 24
  • 32
0

I ran into problems setting a property in a parent class from a child class. The following workround extends a property of a parent but does so by calling the _set_age method of the parent directly. Wrinkled should always be correct. It is a little javathonic though.

import threading


class Foo(object):
    def __init__(self):
        self._age = 0

    def _get_age(self):
        return self._age

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

    age = property(_get_age, _set_age)


class ThreadsafeFoo(Foo):

    def __init__(self):
        super(ThreadsafeFoo, self).__init__()
        self.__lock = threading.Lock()
        self.wrinkled = False

    def _get_age(self):
        with self.__lock:
             return super(ThreadsafeFoo, self).age

    def _set_age(self, value):
        with self.__lock:
            self.wrinkled = True if value > 40 else False
            super(ThreadsafeFoo, self)._set_age(value)

    age = property(_get_age, _set_age)
andrew pate
  • 3,833
  • 36
  • 28
0

I think the answer from Vladimir Zolotykh is nearly optimal.
For my unterstanding a slightly better variant might be calling the constructer of the superclass.
Resulting in the following code:

class Foo:
    def __init__(self, age):
        self.age = age

    @property
    def age(self):
        print("Foo: getting age")
        return self._age

    @age.setter
    def age(self, value):
        print("Foo: setting age")
        self._age = value


class Bar(Foo):
    def __init__(self, age):
        super().__init__(age)


if __name__ == "__main__":
    a = Foo(11)
    print(a.age)
    b = Bar(44)
    print(b.age)

With this solution, there is no need to reeimplement the property for the subclass. Just tell the subclass, that it should behave like the superclass by using the constructor.

The upper lines result in the following output

Foo: setting age
Foo: getting age
11
Foo: setting age
Foo: getting age
44

with python3.

Bastian Ebeling
  • 1,138
  • 11
  • 38
-1
class Foo:
    # Template method
    @property
    def age(self):
        return self.dothis()
    # Hook method of TM is accessor method of property at here
    def dothis(self):
        return 11
class Bar(Foo):
    def dothis(self):
        return 44

Same as Nizam Mohamed, just to mention that style guide 2.13.4 using both template method and property

Robin
  • 33
  • 4