Understand and implement encapsulation using private attributes and public methods (getters and setters) in a Python class. By the end of this lab, you will be able to define a class that securely encapsulates its data, controlling access through specific methods, enhancing data integrity and flexibility in handling data.
Ensure that you are signed into your GitHub account
Join the GitHub Classroom and accept this assignment: Project 12 - Inventory System
Clone the repository to your computer using GitHub Desktop or open it online using Codespaces
Encapsulation is one of the fundamental principles of object-oriented programming. It involves bundling the data (attributes) and methods (functions) that operate on the data into a single unit or class and restricting access to some of the object's components. This means that the internal state of an object is hidden from the outside, only accessible through the object's methods. Think of it as a way of protecting sensitive data from unintended interference and misuse.
Consider this code snippet for a Ticket class:
class Ticket:
def __init__(self, ticket_id, event_date, price):
self.ticket_id = ticket_id
self.event_date = event_date
self.price = price
Without encapsulation, anyone can change the price or date of a ticket directly, potentially setting it to an invalid or illogical value, like a negative price or a past date. This exposes the data to risks of being inconsistent and unreliable.
Private attributes are those that should not be accessed directly from outside the class. In Python, we denote these by prefixing the attribute name with an underscore (_). While this doesn't enforce strict privacy (like in some other languages), it's a strong convention in Python to signal that these attributes are meant to be protected.
class Ticket:
def __init__(self, ticket_id, event_date, price):
self._ticket_id = ticket_id
self._event_date = event_date
self._price = price
Here we convert the public attributes of the Ticket class to private by prefixing them with an underscore. This is a convention in Python to indicate that these should not be accessed directly from outside the class.
Making attributes private ensures that they can only be accessed and modified through methods specifically designed for that purpose (getters and setters). This helps maintain the integrity of the data.
Getters are public methods that allow reading of private attributes. They act as controlled access points to an object’s data, similar to viewing a piece of art in a museum through protective glass. You can see and appreciate the art without touching (and potentially damaging) it.
Using getters means you can maintain an object's integrity while still allowing data to be accessed as needed. For example, if you decide later to change how data is stored or calculated internally, you can do so without affecting other parts of your program that use this data.
class Ticket:
def __init__(self, ticket_id, event_date, price):
self._ticket_id = ticket_id
self._event_date = event_date
self._price = price
def get_ticket_id(self):
return self._ticket_id
def get_event_date(self):
return self._event_date
def get_price(self):
return self._price
Getters can transform the data as it’s accessed, providing a formatted or derived version of the data. For instance, formatting a date in a human-readable form or adjusting a price display to include a currency symbol enhances usability and presentation without altering the underlying data.
import datetime
class Ticket:
def __init__(self, ticket_id, event_date, price):
self._ticket_id = ticket_id
self._event_date = event_date
self._price = price
def get_ticket_id(self):
return self._ticket_id
def get_event_date(self):
return self._event_date.strftime('%d-%m-%Y')
def get_price(self):
return f"${self._price:.2f}"
Setters control the modification of private attributes. They are like gatekeepers or filters, ensuring that only valid and appropriate data is stored. By using setters, a class can enforce rules about how its data should be modified. The primary purpose of basic setters is to encapsulate the process of modifying internal data, even if initially it doesn't include data validation. This encapsulation ensures that all changes to the attribute go through a single, unified interface, making future modifications (like adding validation) easier and safer.
class Ticket:
def __init__(self, ticket_id, event_date, price):
self._ticket_id = ticket_id
self._event_date = event_date
self.set_price(price)
# Basic setter
def set_price(self, new_price):
self._price = new_price
Once the basic functionality of setters is understood, we can enhance them to include validations and checks, ensuring the integrity and correctness of the data being set. Advanced setters act as both gatekeepers and processors, not only setting values but also validating them against defined criteria or rules.
For example, a setter for a price attribute might include checks to ensure the price isn't set to a negative value, reflecting the logical expectation that a ticket cannot have a negative cost. Similarly, setting a date for an event might include checks to ensure the date is in the future, which is a typical requirement for event planning.
These validations help prevent errors and inconsistencies in your application’s data by catching incorrect inputs at the source, thus enforcing business rules directly within the data model.
import datetime
class Ticket:
def __init__(self, ticket_id, event_date, price):
self._ticket_id = ticket_id
self.set_event_date(event_date)
self.set_price(price)
# Advanced setter with validation
def set_price(self, new_price):
if new_price < 0:
raise ValueError("Price cannot be negative.")
self._price = new_price
def set_event_date(self, new_date):
if new_date < datetime.date.today():
raise ValueError("Event date must be in the future.")
self._event_date = new_date
In this class diagram we show the meaning of the '-' and '+' prefixes:
This denotes a 'private' or 'protected' attribute or method
This denotes a 'public' attribute or method
This code demonstrates the use of private attributes and public methods to safely manage ticket data. By utilising advanced setters, the Ticket class enforces rules such as preventing negative prices and ensuring future dates for events, which are crucial for maintaining the logical integrity of the ticketing system.
Open lab1.py
Complete the following activities:
Define the Item class with initialisation that uses setters for name, price, and quantity.
Implement a getter for the name attribute.
Implement a setter for the name attribute.
Implement a getter for the price attribute.
Implement a setter for the price attribute.
Implement a getter for the quantity attribute.
Implement a setter for the quantity attribute.
Create instances of the Item class and demonstrate the use of getters and setters.
Commit and push your code