pythonnew-style-class

What is the purpose of Python's property.getter?


Since Python strives for there to be one right way, I'm wondering what the purpose of property.getter is. In this example WhyMe defines a getter but Other doesn't so I'm wondering what the point of property.getter is over just using property.

class WhyMe(object):
    def __init__(self):
        self._val = 44

    @property
    def val(self):
        print 'I am not called'
        return self._val

    @val.getter # What advantage do I bring?
    def val(self):
        print 'getter called'
        return self._val

class Other(object):
    def __init__(self):
        self._val = 44

    @property
    def val(self):
        print 'I AM called'
        return self._val

And using them:

>>> why = WhyMe()
>>> why.val
getter called
44
>>> other = Other()
>>> other.val
I AM called
44

I'm no stranger to properties, I'm just wondering if there is some advantage to making a getter or if was just put there for symmetry?


Solution

  • @property let's you define a whole new property, by defining a getter for that property. The @original.getter syntax lets you override just the existing getter. There are also .setter and .deleter decorators, to the other two methods available to a property.

    Imagine you subclassing a custom class that is using a property, with both a getter and a setter defined. If you only wanted to override the getter of that property but leaving the setter in place (or the deleter), using @BaseClass.original.getter lets you do that:

    >>> class Foo(object):
    ...     @property
    ...     def spam(self):
    ...         print 'Foo.spam called'
    ...         return 'spam'
    ...     @spam.setter
    ...     def spam(self, value):
    ...         print 'Foo.spam.setter called'
    ...     @property
    ...     def ham(self):
    ...         print 'Foo.ham called'
    ...         return 'ham'
    ...     @ham.setter
    ...     def ham(self, value):
    ...         print 'Foo.ham.setter called'
    ... 
    >>> class Bar(Foo):
    ...     @Foo.spam.getter
    ...     def spam(self):
    ...         print 'Bar.spam override'
    ...         return 'eggs and spam'
    ...     @property
    ...     def ham(self):
    ...         print 'Bar.ham override'
    ...         return 'eggs and ham'
    ... 
    >>> Bar().spam
    Bar.spam override
    'eggs and spam'
    >>> Bar().spam = 'foo'
    Foo.spam.setter called
    >>> Bar().ham
    Bar.ham override
    'eggs and ham'
    >>> Bar().ham = 'foo'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute
    

    Note that I only replaced the spam getter, while it's setter was preserved. The Bar.ham setter does not exist, I replaced the Foo.ham property wholesale.