corvus
corvus

Reputation: 595

When should I define getter methods for python properties?

I am a little confused about the utility of the python @*.getter syntax versus simply defining a property without a getter.

To investigate this, let's write a very simple class:

class Foo:

    def __init__(self):
    
        self._x = None

    @property
    def x(self):
        print('Attribute `self.x` is a property.')
        return self._x

    @x.getter
    def x(self):
        print('Getting property `x`...')
        return self._x

Now, if I try to instantiate the class and get the property x from the instance, we see that the getter method is called:

>>> A = Foo()
>>> print(A.x)
Getting property `x`...
None

Okay, so what? That's exactly what we ought to expect.

However, if I define the exact same class without any getter method, we get something different:

>>> print(A.x)
Attribute `self.x` is a property.
None

When no getter method is defined, attempting to retrieve the property results in the 'property definition method' (I mean the method defined under @property, for lack of a better name) getting called instead.

Interestingly, in the first case, the method defined under @property is never actually called.

So, then, what is the point of having a special getter method if the original property definition seems to serve as a getter in its absence? Does the original definition serve any purpose other than simply to say, "this is a property"? Are there any other circumstances when this method would be called (besides property retrieval in the absence of a getter)? Why would you choose to use a getter versus simply incorporating its functionalities into the property definition?

Lastly, are there any use cases when one would use a property definition method that is any more complex than the following canonical form, while also using an @*.getter method?

@property
def x(self): return self._x

Upvotes: 1

Views: 104

Answers (1)

Grismar
Grismar

Reputation: 31354

Using the @x.getter decorator tells Python that the following method is the intended getter for the property.

You're right that it's not very useful in the example you're giving. The @property decorator does the same job and defines the property all in one go. There really is no reason to use both in a single class definition.

However, perhaps you want to override the getter method in a subclass?

class Foo:
    def __init__(self, x):
        self._x = x

    @property
    def x(self):
        print('Attribute `self.x` is a property.')
        return self._x


class Bar(Foo):
    count: int = 0

    @Foo.x.getter
    def x(self):
        Bar.count += 1
        print(f'Getting Bar property `x` ({Bar.count})')
        return self._x


foo = Foo(10)
print(foo.x)
bar = Bar(10)
print(bar.x)
print(bar.x)

Output:

Attribute `self.x` is a property.
10
Getting Bar property `x` (1)
10
Getting Bar property `x` (2)
10

There are several other use cases where defining the getter explicitly makes sense, even though in the base case you just use @property and @attr.setter.

The counter is just there to show a simple reason why you might want to modify the getter behaviour, even if it doesn't modify the actual value of the property.

Upvotes: 1

Related Questions