In object-oriented programming, getter and setter methods allow controlled access to an object’s attributes. Getters and setters are also known as accessors and mutators. In Python, getter and setter methods provide an interface for retrieving and updating an object’s state while hiding the implementation details. This encapsulation mechanism helps prevent direct access to object attributes, making the code more robust and maintainable.
This comprehensive guide will explain what getter and setter methods are, why they are used, how to implement them in Python, and provide examples demonstrating their usage. Best practices for designing getter and setter methods will also be covered. By the end, you will have a solid understanding of using getter and setter methods for controlled attribute access in Python.
Table of Contents
Open Table of Contents
What are Getter and Setter Methods?
Getter and setter methods are special methods that provide indirect access to an object’s attributes. Here are their key characteristics:
-
Getters - Used to retrieve the value of an attribute. Also known as accessors.
-
Setters - Used to set or update the value of an attribute. Also known as mutators.
-
Encapsulation - Getters and setters encapsulate the implementation details of an object’s attributes by providing an interface to access and modify the attributes in a controlled fashion.
-
Data Hiding - Direct access to attributes is prevented which hides the internal representation of the object’s state.
-
Access Control - Getters and setters facilitate access control over read and write operations on attributes. Additional logic can be incorporated.
By convention, getters and setters in Python are named starting with get_
and set_
respectively followed by the attribute name.
Why Use Getter and Setter Methods?
Here are some key reasons to use getter and setter methods in Python:
-
Control attribute access - Prevents direct access to attributes, making future changes easier.
-
Validation logic - Logic can be added to validate data before setting it.
-
Read-only attributes - Getters without setters can make attributes read-only.
-
Derived attributes - Getters can compute and return derived attribute values.
-
Encapsulation - Implementation details can be hidden and changed without affecting code using the class.
-
Maintainability - Reduces dependencies between code sections making maintenance easier.
-
Access restrictions - Getters and setters permit access restrictions to be added later.
Overall, getter and setter methods make code more robust, extensible, and maintainable by controlling access to object attributes.
Implementing Getters and Setters in Python
In Python, getter and setter functionality can be implemented using the @property
decorator. Here is the general syntax:
class ClassName:
def __init__(self):
self._attribute = 0
@property
def attribute(self):
return self._attribute
@attribute.setter
def attribute(self, value):
self._attribute = value
The @property
decorator is used to create getter functions that allow attribute access using dot notation like a normal attribute.
The @<attribute>.setter
decorator applied on a method converts it to a setter function for that attribute.
Let’s see an example class implementing getter and setter methods:
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('Name must be a string')
self._name = value
person = Person('John')
# Getter usage
print(person.name)
# Setter usage
person.name = 'Sam'
The name
attribute is accessed through getter and setter methods instead of directly. This allows validation and constraints to be added like ensuring name is always a string.
Read-Only Attributes
To create a read-only attribute, simply create a getter without a corresponding setter:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def diameter(self):
return 2 * self.radius
Now diameter
can be accessed like a normal attribute but cannot be modified as no setter exists.
Derived Attributes
Getters can also compute derived attribute values each time they are accessed:
class Person:
def __init__(self, age):
self.age = age
@property
def age_in_months(self):
return self.age * 12
Best Practices for Getters and Setters
Here are some best practices to follow when implementing getter and setter methods in Python:
-
Give descriptive names like
get_name()
andset_name()
following conventions. -
Use
@property
decorator for getter methods and@<attribute>.setter
for setters. -
Make attributes private by prefixing with an underscore like
_name
. -
Perform validation and data cleaning in setter before saving values.
-
Raise exceptions on invalid attribute values instead of failing silently.
-
Make attributes read-only by implementing only a getter if attribute should not be changed.
-
Add documentation strings to explain usage of non-obvious getters and setters.
-
Avoid unnecessary getter and setter methods that just directly access attributes.
-
Implement caching in getter methods if computations are expensive.
-
Follow style guidelines - PEP 8 recommends one line for simple getters and setters.
-
Avoid side effects in getters and setters like I/O which can be confusing.
By properly designing getters and setters methods following best practices, you can create Python classes with controlled attribute access.
Example Use Cases
Here are some examples demonstrating real-world usage of getter and setter methods:
Validating Input
Getters and setters allow validating data before setting it:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError('Age cannot be negative')
self._age = value
person = Person('Mary', -10) # Raises ValueError
Read-only DB ID
For an object fetched from a database, the ID can be read-only while other attributes are changeable:
class DBObject:
def __init__(self, id, name, value):
self.id = id
self.name = name
self.value = value
@property
def id(self):
return self._id
@id.setter
def id(self, value):
raise AttributeError('ID is read-only')
Derived Attribute
Getters can be used to compute and return derived attributes:
import math
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def diameter(self):
return 2 * self.radius
@property
def area(self):
return math.pi * self.radius ** 2
Cached Lookup
For expensive computations, getters can cache the results to avoid recomputing each lookup:
class Image:
def __init__(self, path):
self.path = path
self._size = None
@property
def size(self):
if self._size is None:
#expensive size lookup
self._size = get_image_size(self.path)
return self._size
Conclusion
Getter and setter methods in Python provide controlled attribute access through an interface decoupling object implementation from usage. This leads to more maintainable and robust code.
The @property
decorator allows implementing getters and setters elegantly in Python without much boilerplate code. Getters return attribute values while setters modify them.
Key benefits include data validation, derived attributes, read-only attributes, encapsulation, and access control. By following Pythonic best practices for getter and setter design, you can create classes with proper controlled access to attributes.
In summary, getter and setter methods are a crucial object-oriented programming concept in Python enabling encapsulation and access control for maintainable and robust software development.