Usually when public/private are talked about in terms of object design it is about who is allowed to access what external to the class. From this perspective it does not matter since these methods are "internal" to the class (or the module, etc). But that doesn't mean there isn't some nuance.
For the most basic usage of this - using a property to restrict the mutability of a field (only having a "getter" and no "setter") - it is probably better to use the property in other parts of the class. That way its mildly easier to scan and confirm that the property isn't being set in any methods.
class Pos:
def __init__(self, x, y):
self._x = x
self._y = y
@property
def x():
return self._x
@property
def y():
return self._y
def hypotenuse(self):
# probably doesn't matter either way
return ((self.x * self.x) + (self.y * self.y))**(1/2)
There are other potential usages where it might be inappropriate to do this. For example, say you have a class that maintains some "list of things". It would be a reasonable design choice to make that list be a "private" thing and create a property that would make a clone of the list when accessed. (That way no caller could call .append() and affect its internal state). In this situation, you would want to use the private version to avoid making that copy.
class ThingHolder:
def __init__(self):
self._things = [1, 2, 3]
@property
def things(self):
return self._things[:]
def print_things(self):
for thing in self._things:
print(thing)
Accessing the public one instead of the private also makes it easier to later refactor to doing things like the "mixin pattern".
# If you start with this
class Dog:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
def speak(self):
print("I am " + self.name)
# It might be mildly easier to refactor to this
# if you want to for whatever reason
class NamedSpeaker:
def speak(self):
print("I am " + self.name)
class Dog(NamedSpeaker):
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
class Person(NamedSpeaker):
def __init__(self, first_name, last_name):
self._first_name = first_name
self._last_name = last_name
@property
def name(self):
return self._first_name + " " + self._last_name
I'm sure there are more examples of where it might swing one way or another, I just can't think of many.