1

i was looking into properties and could not understand the use of getter.

Code :

class A:
    def __init__(self,p):
        self._p = p
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, value):
        self._p = value
    @p.getter
    def p(self):
        return self._p

so,i did the following

obj = A(10)
print(obj.p)

i got the following output:

10

So, i thought, @p.getter is called and the value of _q is returned.

But, i tried without @p.getter as below

class A:
    def __init__(self,p):
        self._p = p
    @property
    def p(self):
        return self._p
    @p.setter
    def p(self, value):
        self._p = value

And did the following

obj = A(10)
print(obj.p)

and even then i got the following output:

10

So, i was wondering, what is the actual use of @p.getter here, when @property it self was able to give us the value of _q

rawwar
  • 4,834
  • 9
  • 32
  • 57

3 Answers3

4

The @property.getter decorator can be useful if you want to override a getter from a parent class in a subclass. It allows you to easily "copy" a property from the parent class and override only the getter, keeping the setter and deleter intact.

Example:

class Parent:
    @property
    def p(self):
        return 'parent'

    @p.setter
    def p(self, value):
        print('setting p to', value)

    @p.deleter
    def p(self):
        print('deleting p')

class Child(Parent):
    @Parent.p.getter
    def p(self):
        return 'child'

print(Parent().p)  # output: parent
print(Child().p)   # output: child
Child().p = 3      # output: setting p to 3
del Child().p      # output: deleting p
Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
3

The @property decorator on a function creates a new property object and sets the decorated function as the getter. So within the same class, the @p.getter decorator is not very useful, no.

However, the decorator is very useful if you wanted to override the getter in a subclass, or a reuse just the setter of the property on, but on a different class altogether:

class Foo(object):
    @property
    def p(self):
        return self._p

    @p.setter
    def p(self, value):
        self._p = value

class Bar(Foo):
    @Foo.p.getter
    def p(self):
        return self._p + 10

or by copying the property object across to a different class:

class NotASubclass(object):
    @Foo.p.getter
    def p(self):
        return self._p * 10

Now Bar()and NotASubclass have a different getter for their property p, but both reuse the setter defined on the property for Foo().

In both cases the @p.getter decorator creates a new property instance to store as an attribute of the class object, but their fset attributes all point to the same, single function object that acts as the setter. This is why it is enough to just reference @Foo.p.getter in either case; you only need the single property instance in the class namespace.

Also see How does the @property decorator work? and Python overriding getter without setter

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks, second example is great – rawwar Jul 09 '18 at 11:35
  • property by default return themselves because in the actual implementation `def __get__(self, obj, objtype=None): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") return self.fget(obj)` , if the getter is not defined, it return's itself.Because of that i was able to get the value of _p Am i right? – rawwar Jul 09 '18 at 12:36
  • 1
    @InAFlash: no, if the getter is not defined, an `AttributeError` exception is raised. `obj` is the instance to which to bind, if that's `None` the access was on the class. So `ClassObj.property_name` gives you the property object itself (because the [descriptor protocol](https://docs.python.org/3/howto/descriptor.html) then calls `ClassObj.__dict__['property_name'].__get__(None, ClassObj)`), and `instance.property_name` gives you the result of the getter function (because then `ClassObj.__dict__['property_name'].__get__(instance, ClassObj)`)` is called). – Martijn Pieters Jul 10 '18 at 08:36
0

As you've noticed, in this case, it's totally useless since you already passed the getter here

@property
def p(self):
    return self._p

but it might be useful for a couple corner cases at least, like dynamically redefining the getter for an existing property. I have to say I never had such a use case actually but well...

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118