22. OOP ‐ Encapsulation - MantsSk/CA_PTUA14 GitHub Wiki
As we already know, Python
is an Object-Oriented Programming language. Everything in Python is an object. Like other Object-Oriented languages, when creating objects using classes, there are 4 basic principles for writing clean and concise code. These principles are called the four pillars of object-oriented programming (OOP). These four pillars are: Inheritance, Polymorphism, Encapsulation and Abstraction.
A few to name:
- It makes it so much easier to maintain and update existing code.
- Provides code reusability.
- Provides a modular structure.
- It is easy to debug.
In most of the object-oriented languages access modifiers are used to limit the access to the variables and functions of a class. Most of the languages use three types of access modifiers, they are - private, public and protected.
Just like any other object oriented programming language, access to variables or functions can also be limited in python using the access modifiers. Python makes the use of underscores
to specify the access modifier for a specific data member and member function in a class.
Access modifiers play an important role to protect the data from unauthorized access as well as protecting it from getting manipulated. When inheritance (look at the following chapter ⬇️ ) is implemented there is a huge risk for the data to get destroyed(manipulated) due to transfer of unwanted data from the parent class to the child class. Therefore, it is very important to provide the right access modifiers
for different data members and member functions depending upon the requirements.
There are 3 types of access modifiers for a class in Python. These access modifiers define how the members of the class can be accessed.
These are:
- Public - the members declared as
Public
are accessible from outside theclass
through an object of theclass
. - Protected - the members declared as
Protected
are accessible from outside theclass
but only in aclass
derived from it that is in the child or subclass. - Private - these members are only accessible from within the class. No outside
Access
is allowed.
Public
# defining a class Employee
class Employee:
# constructor
def __init__(self, name, sal):
self.name = name # Public attribute
self.sal = sal
---- OUTPUT ----
>>> emp = Employee("Ironman", 999000);
>>> emp.sal
999000
👨🏫 ❗ Attention ❗ By default, all the variables and member functions of a class are public in a python program.
Protected
# defining a class Employee
class Employee:
# constructor
def __init__(self, name, sal):
self._name = name # Protected attribute
self._sal = sal # Protected attribute
---- OUTPUT ----
>>> emp = Employee("Captain", 10000);
>>> emp.sal
10000
Private
# defining a class Employee
class Employee:
# constructor
def __init__(self, name, sal):
self.__name = name # Private attribute
self.__sal = sal # Private attribute
---- OUTPUT ----
>>> emp = Employee("Bill", 10000);
>>> emp.__sal;
AttributeError: 'employee' object has no attribute '__sal'
ℹ️ There are private and protected methods too. The working mechanism of access modifiers applies the same as for the class attributes: A private method can be only called inside the particular class, a protected method inside the particular class and by its child classes.
Private method
class Example:
def __init__(self):
self.__a = "This is a private variable"
def __can_not_run(self):
print("Can't be executed outside of this class")
>>> ex = Example()
>>> print(ex.__a)
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Example' object has no attribute '__a'>>> ex.__can_not_run()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Example' object has no attribute '__can_not_run'
Protected method
class User:
def __init__(self,username) -> None:
self.username = username
def _check_account_existence(self):
print(f"Checking if {self.username} has signed up already.")
---- OUTPUT ----
>>> user = User("cowboy")
>>> user._check_account_existence()
Checking if cowboy has signed up already.
💁 Reminder:
- Protected methods use one underscore as their prefix, while private methods use two underscores as their prefix.
- We can call protected methods outside the class directly by using their method names. However, calling private methods requires name mangling.
Besides these two obvious differences between protected and private methods, the major difference between these two kinds of methods_ pertains to their accessibility within their subclasses_. That is, protected methods are accessible within the subclasses, while private methods are not accessible within the subclasses (although the reason is also due to name mangling). ℹ️
Encapsulation is the process of hiding the data, providing security to data by making the variable protected. The protected member can only be accessed by the class member. If you try to access it outside the class normally, by creating an object. it will result in an error. To access the protected member you need to use object._protectedmember
.
In Python, to create a protected member, we use the convention of prefixing the name of the member by a single underscore, e.g., _name.
Encapsulation protects an object from unwanted access by clients. It reduces the chances of human error and also simplifies the maintenance of the application. Encapsulation allows access to a level without revealing the details: 🔽
class Parent: ## Creating a class name Parent
def __init__(self): ## Constructor of parent class
# protected member
self._mobilenumber = 5555551234 ## Protected member of the class Parent
class Child(Parent): ## Child class inhering properties from the Parent class
def __init__(self): ## Constructor of the class name
Parent.__init__(self) ## accessing members of the Parent class, another way is to used super()
print("Calling Protected Member")
print(self._mobilenumber) ## accessing protected member using the class member
obj = Child() ## creating the object
print(obj.mobilenumber) ## AttributeError: 'Child' object has no attribute 'mobilenumber'
print(obj._mobilenumber) ## Prints mobilenumber, explicitly allowing the access to protected member
_mobilenumber
is a protected member of the class that can only be accessed by the class members and object after giving explicit permission to the object.
A very simple example of encapsulation:
class BankAccount:
def __init__(self, initial_balance):
self.__balance = initial_balance # Private attribute
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return f"Deposited {amount}. New balance is {self.__balance}."
return "Invalid deposit amount."
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
return f"Withdrew {amount}. New balance is {self.__balance}."
return "Invalid withdrawal amount or insufficient funds."
def get_balance(self):
return f"Current balance is {self.__balance}."
# Using the class
account = BankAccount(1000)
print(account.deposit(500))
print(account.withdraw(200))
print(account.get_balance())
Notice how we have private attribute and we only access it or set it trough methods
🧠 : Repeat the Conditional Statements, Loops, Functions to finish these task.
- Create a Python class called BankAccount with a private attribute balance. Implement methods deposit and withdraw to modify the balance. Ensure that the balance cannot be accessed directly from outside the class. Create an instance of the BankAccount class, deposit some money, withdraw some money, and print the remaining balance.
- Create a base class called Shape with protected attributes width and height. Implement a method calculate_area in the Shape class that calculates and returns the area. Create a derived class Rectangle that inherits from the Shape class. Override the calculate_area method in the Rectangle class to calculate the area specifically for a rectangle. Create an instance of the Rectangle class, set its dimensions, and print the calculated area.
- Create a Python class called Person with private attributes name and age. Implement methods get_name and get_age to access these private attributes. Create a derived class Employee that inherits from the Person class. Add a private attribute salary to the Employee class. Implement methods get_salary and set_salary to access and modify the salary. Create an instance of the Employee class, set the name, age, and salary, and print the information.