Encapsulation and Abstraction are two foundational pillars of Object-Oriented software design. Together, they protect internal object integrity, hide internal implementation complexity, and expose clean, intuitive interfaces to external consumers.
1. What is Encapsulation?
Encapsulation is the bundling of data (attributes) and the methods that operate on that data into a single unit (a class), while restricting direct access to internal components to prevent unintended tampering.
Python Naming Conventions for Access Control
Unlike languages with hard public, protected, and private keywords, Python uses naming conventions and name mangling:
Type
Convention
Meaning
Access Behavior
Public
attribute
Part of the public API
Fully accessible from anywhere
Protected
_attribute
Internal use only; intended for class & subclasses
Accessible, but signals "do not touch outside class"
Python's Philosophy: Python operates on the principle of "We are all consenting adults here". Name mangling is designed primarily to avoid naming collisions in subclasses, not as an impenetrable security sandbox.
2. Pythonic Properties: @property and Setters
In traditional OOP (like Java or C++), developers write verbose getBalance() and setBalance() methods. Python replaces this with the elegant @property decorator, enabling getter/setter validation while preserving clean attribute access syntax (acc.balance = 500 instead of acc.set_balance(500)).
1classEmployee:
2def__init__(self, name:str, salary:float):
3 self.name = name
4 self.salary = salary # Uses the setter below!
5
6# 1. Getter property
7@property
8defsalary(self)->float:
9return self._salary
10
11# 2. Setter with input validation
12@salary.setter
13defsalary(self, value:float):
14ifnotisinstance(value,(int,float)):
15raise TypeError("Salary must be a numeric value.")
16if value <0:
17raise ValueError("Salary cannot be negative.")
18 self._salary =float(value)
19
20# 3. Deleter (optional)
21@salary.deleter
22defsalary(self):
23print(f"Deleting salary record for {self.name}.")
24del self._salary
25
26emp = Employee("Meera",75000)
27print(emp.salary)# 75000.0 (calls getter)
28
29emp.salary =82000# Validated update via setter
30print(emp.salary)
31
32try:
33 emp.salary =-500# Raises ValueError: Salary cannot be negative.
34except ValueError as err:
35print(f"Validation caught error: {err}")
3. What is Abstraction?
Abstraction focuses on hiding background details and displaying only essential features to the user. An end-user interacting with a smartphone screen doesn't need to understand RF transmission circuits or silicon microcode—they only interact with high-level buttons and icons.
In Python, abstraction is enforced using the abc (Abstract Base Classes) module:
ABC: Base class from which abstract classes inherit.
@abstractmethod: Decorator indicating methods that must be implemented by any concrete subclass.
1from abc import ABC, abstractmethod
2
3# Abstract Base Class definition
4classDatabaseConnector(ABC):
5@abstractmethod
6defconnect(self):
7"""Establish connection to data store."""
8pass
9
10@abstractmethod
11defexecute_query(self, query:str):
12"""Execute a query string and return results."""
13pass
14
15@abstractmethod
16defdisconnect(self):
17"""Cleanly terminate connection."""
18pass
19
20
21# Attempting to instantiate an abstract class directly fails:
22try:
23 db = DatabaseConnector()
24except TypeError as err:
25print(f"Direct instantiation blocked: {err}")
4. Implementing Concrete Subclasses
Any subclass that fails to implement all abstract methods cannot be instantiated:
1classPostgreSQLConnector(DatabaseConnector):
2defconnect(self):
3print("Connected to PostgreSQL on port 5432.")
4
5defexecute_query(self, query:str):
6print(f"Executing SQL: '{query}' on Postgres engine.")
7return[{"id":1,"status":"active"}]
8
9defdisconnect(self):
10print("PostgreSQL connection terminated.")
11
12# Concrete class instantiated successfully
13pg = PostgreSQLConnector()
14pg.connect()
15data = pg.execute_query("SELECT * FROM users;")
16pg.disconnect()
⚠️ Common Intermediate Mistakes & Gotchas
1. Believing Double Underscores (__) Make Attributes Truly Private
Python does not enforce true private memory. Double underscores simply trigger name mangling (_ClassName__attribute). Use single underscore (_attr) for protected convention, and double underscores only when avoiding subclass name collisions.
2. Defining Setter Without Getter in @property
A @prop.setter requires the matching @property getter to be defined first with the exact same method name.
💻 Try It Yourself: Temperature Sensor with @property
Scenario
Build a TemperatureSensor class where the internal temperature is stored in Celsius, but provides a getter and setter in Fahrenheit using @property.
Complete Solution
1classTemperatureSensor:
2def__init__(self, celsius:float=0.0):
3 self._celsius =float(celsius)
4
5@property
6defcelsius(self)->float:
7return self._celsius
8
9@celsius.setter
10defcelsius(self, value:float):
11if value <-273.15:
12raise ValueError("Temperature below absolute zero is physically impossible!")
26print(f"Set to 98.6°F -> Celsius is: {sensor.celsius:.1f}°C")
Expected Output
1Temperature: 25.0°C = 77.0°F
2Set to 98.6°F -> Celsius is: 37.0°C
Interactive Quiz• Knowledge Check
Practice Quiz
Question 1
What happens when an attribute name begins with two leading underscores (e.g. __secret)?
Question 2
Which decorator is used to turn a method into a read-only getter attribute?
Question 3
What error is raised when attempting to instantiate an abstract class that has unimplemented @abstractmethods?
Question 4
What is the primary convention of a single leading underscore (e.g. _internal_var)?
Question 5
From which standard library module are ABC and @abstractmethod imported?
🚀 What's Next?
In the next lesson, we will continue your intermediate Python journey with Project: Bank Account Class System (5: Object-Oriented Programming (Intermediate)).