In object-oriented programming, the getter and setter methods help us access private attributes of a class, allowing the code to be well encapsulated and to avoid accessing them directly.
Although in Python we can't define methods or attributes as private, we can use a convention that indicates to other developers the level of accessibility of these.
- Protected: For protected attributes or methods, we must set an underscore before the definition. e.g.
self._name = "My name"
- Private: Private attributes or methods have two underscores. e.g.
def __private_method(self):
class User:
def __init__(self, name, age):
self.__name = name
self.__age = age
def get_name(self):
return self.__name
def set_name(self, name):
self.__name = name
user = User("Esteban", 26)
print(f"Name: {user.get_name()}")
user.set_name("Cristian")
print(f"Name: {user.get_name()}")
# Output:
# Name: Esteban
# Name: Cristian
The traditional way or as it is normally used in other programming languages is to use normal methods.
This preamble is intended to give you an understanding of each of these levels and to help you understand how Property can help you create getters and setters in a Pythonic way.
Property
is a built-in decorator that allows us to define the getter and setter.
Let's see with the same code as above how to use the property decorator:
class User:
def __init__(self, name, age):
self.__name = name
self.__age = age
@property
def name(self):
print("Getter called")
return self.__name
@name.setter
def name(self, name):
print("Setter called")
self.__name = name
user = User("Esteban", 26)
print(f"Name: {user.name}")
user.name = "Cristian"
print(f"Name: {user.name}")
# Output:
# Getter called
# Name: Esteban
# Setter called
# Getter called
# Name: Cristian
As you can see, the property
decorator did not change much in the way we do the getter/setter, however this allows it to be more intuitive and easy to read, also like the methods defined above, allow us to control how we access the attributes of the class.
The property decorator is defined in a method that will return the value of the attribute, to define the setter we must use the property created, @<property>.setter
.
We don't have to define each of these methods for every property in our classes, as we only do this for those we're going to access, or if they can be read-only attributes we'll only need the getters.
Remember that we can implement these methods as we need them so that we won't affect the implementation of the code.
class User:
def __init__(self, name, age):
self.__name = name
self.__age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, age):
if not isinstance(age, int) or age < 0:
raise ValueError("Invalid age")
self.__age = age
user = User("Esteban", 26)
user.age = "26"
# Output:
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 13, in age
# ValueError: Invalid age