No, there is absolutely no use for a property here. The entire point of property is to avoid boilerplate and maintain encapsulation.
A Pythonic class definition
Suppose you write a class:
class Circle:
def __init__(self, radius):
self.radius = radius
Great. Now, there's a bunch of client code out there like:
print(f"The radius is {some_circle.radius}")
An un-Pythonic class definition
Suppose, however, you want to make sure the radius is never set to a value below 0. Oh no! What should we do? The philosophy in languages like Java is that you should have written boilerplate getters and setters like this in the first place:
class Circle:
def __init__(self, radius):
self.set_radius(radius)
def get_radius(self):
return self.radius
def set_radius(self, radius):
self._radius = radius
Then, all the client code would have been written like:
print(f"The radius is {some_circle.get_radius()}")
And we can just modify set_radius to throw an appropriate error:
class Circle:
def __init__(self, radius):
self.set_radius(radius)
def get_radius(self):
return self.radius
def set_radius(self, radius):
if radius < 0:
raise ValueError("Radius must be positive")
self._radius = radius
All that boilerplate at the beginning was worth it because now we can make changes to the way we control access to our internal state without breaking client code.
But this isn't Java, this is Python
In Python, if and when we decide to control access, we can refactor our class without breaking client code!:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, radius):
if radius < 0:
raise ValueError("Radius must be positive")
self._radius = radius
Because let's be honest. Most of the time we'd rather write a class like the first example, not the second example with the pre-emptive boilerplate getters and setters, and we like to write client code like:
pi * circle.radius**2
instead of
pi * circle.get_radius()**2
In Python, we can have our cake and eat it too.
An exception
There is one use case for a property like in your code: we want to let the clients of our class retrieve a value but not set the value. So just like your example:
>>> class Foo:
... def __init__(self):
... self._foo_value = 'foo_value'
... @property
... def foo_value(self):
... return self._foo_value
...
>>> foo = Foo()
>>> foo.foo_value
'foo_value'
>>> foo.foo_value = 'something else'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
If he above is the behavior you want, the the property is actually serving a purpose. Of course, this can be easily circumvented, but it does prevent "accidental" misuse of the API. And, we still get to keep the nicer-looking foo.foo_value instead of the uglier foo.get_foo_value()