Python tutorials > Object-Oriented Programming (OOP) > Encapsulation > What are getters/setters?
What are getters/setters?
Introduction to Getters and Setters
Basic Implementation of Getters and Setters
Person
with private attributes _name
and _age
. We use the underscore prefix (_
) as a convention in Python to indicate that an attribute is intended for internal use and should not be accessed directly from outside the class. Getters (get_name
, get_age
) provide read-only access to these attributes, while setters (set_name
, set_age
) allow modification with input validation.
class Person:
def __init__(self, name, age):
self._name = name # Convention: _name indicates a 'protected' attribute
self._age = age
def get_name(self):
return self._name
def set_name(self, new_name):
if isinstance(new_name, str) and len(new_name) > 0:
self._name = new_name
else:
print("Invalid name")
def get_age(self):
return self._age
def set_age(self, new_age):
if isinstance(new_age, int) and new_age > 0:
self._age = new_age
else:
print("Invalid age")
# Example usage
person = Person("Alice", 30)
print(person.get_name())
person.set_name("Bob")
print(person.get_name())
person.set_age(-5) # Prints "Invalid age"
print(person.get_age())
Using the @property
Decorator
@property
decorator provides a more elegant and Pythonic way to define getters and setters. It allows you to access and modify attributes as if they were directly accessible, while still utilizing the getter and setter methods behind the scenes. This approach enhances readability and maintainability. The @property
decorator defines the getter, and the @
decorator defines the setter. The usage becomes more natural: you can access person.name
directly instead of calling person.get_name()
.
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if isinstance(new_name, str) and len(new_name) > 0:
self._name = new_name
else:
print("Invalid name")
@property
def age(self):
return self._age
@age.setter
def age(self, new_age):
if isinstance(new_age, int) and new_age > 0:
self._age = new_age
else:
print("Invalid age")
# Example usage
person = Person("Alice", 30)
print(person.name) # Access name like an attribute
person.name = "Bob"
print(person.name)
person.age = -5 # Prints "Invalid age"
print(person.age)
Concepts Behind the Snippet
Real-Life Use Case Section
BankAccount
class. You might want to control access to the balance
attribute to prevent direct manipulation that could lead to invalid balances (e.g., negative balances). Getters and setters allow you to implement rules such as preventing withdrawals that would result in a negative balance or logging all balance changes for auditing purposes. Another example is in GUI programming, where a setter for a text field might need to update the displayed text on the screen whenever the underlying data changes.
Best Practices
get_attribute
, set_attribute
).@property
decorator for a cleaner and more Pythonic syntax.
Interview Tip
@property
decorator. Also, be ready to discuss scenarios where using getters and setters is essential.
When to Use Them
Memory footprint
@property
decorator also doesn't introduce a substantial memory overhead. The primary concern is the data type and size of the attributes being managed.
Alternatives
dataclasses
module): Python's dataclasses
module provides a concise way to create classes with automatically generated methods, including getters and setters (though you may need to customize them).collections.namedtuple
): For simple data containers, named tuples offer immutability, which can be beneficial for certain use cases.
Pros
Cons
FAQ
-
Why use getters and setters when I can directly access attributes?
Getters and setters provide encapsulation, allowing you to control how attributes are accessed and modified. This enables validation, modification, and other actions during attribute access, enhancing the robustness and maintainability of your code. -
When should I use the
@property
decorator?
The@property
decorator is preferred when you want a more Pythonic and readable way to define getters and setters. It allows you to access and modify attributes as if they were directly accessible while still using getter and setter methods behind the scenes. -
Are getters and setters always necessary?
No, getters and setters are not always necessary. They are most useful when you need to control access to attributes, validate input, or perform additional actions during attribute access or modification. For simple data objects without such requirements, direct attribute access may be sufficient.