0% found this document useful (0 votes)
4 views11 pages

Understanding Encapsulation in OOP

Chapter Four discusses the principle of encapsulation in object-oriented programming, emphasizing the importance of restricting access to an object's internal data through public interfaces. It covers access modifiers (public, private, protected) and the use of getters and setters to manage data access, as well as the @property decorator for creating properties. The chapter also provides use cases demonstrating how encapsulation enhances data integrity, restricts direct access, and protects sensitive information.

Uploaded by

zv96xfm876
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views11 pages

Understanding Encapsulation in OOP

Chapter Four discusses the principle of encapsulation in object-oriented programming, emphasizing the importance of restricting access to an object's internal data through public interfaces. It covers access modifiers (public, private, protected) and the use of getters and setters to manage data access, as well as the @property decorator for creating properties. The chapter also provides use cases demonstrating how encapsulation enhances data integrity, restricts direct access, and protects sensitive information.

Uploaded by

zv96xfm876
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Object Oriented Programming I Chapter Four

Encapsulation
4.1 Introduction
The most important principle of object orientation is encapsulation. The idea that
data inside the object should only be accessed through a public interface, that is,
the object’s methods. Encapsulation is the process of wrapping a piece of code in a
function, allowing you to take advantage of all the things functions are good for.
OOP enables us to hide the complexity of the internal working of the object which
is advantageous to the developer in the following ways:
• Simplifies and makes it easy to understand to use an object without knowing
the internals.
• Any change can be easily manageable.

Attributes Methods

Class

OOP relies heavily on encapsulation. The terms encapsulation and abstraction (also
called data hiding) are often used as synonyms. They are nearly synonymous, as
abstraction is achieved through encapsulation. Encapsulation provides us the
mechanism of restricting the access to some of the object’s components, this means
that the internal representation of an object can’t be seen from outside of the object
definition. Access to this data is typically achieved through special methods:
Getters and Setters. This data is stored in instance attributes and can be
manipulated from anywhere outside the class. To secure it, that data should only be
accessed using instance methods. Direct access should not be permitted.
In Python, encapsulation is not enforced by the language, but there is a convention
that we can use to indicate that a property is intended to be private and is not part
of the object’s public interface. This can be done by begin the name with an
underscore. It is also customary to set and get simple attribute values directly, and
only write setter and getter methods for values which require some kind of
calculation.

1
Object Oriented Programming I Chapter Four

4.2 Access Modifiers


One of the main concepts in OOP is access modifiers which represent the visibility
and accessibility of methods and attributes. Access modifiers can help in
determining the way that methods, and variables can be accessed and modified.
The encapsulation concept is implemented through access modifiers where the
access to class variables, and methods can be controlled and restricted to
authorized modifiers. In Python, the symbol “_” can be used to define the
accessibility to each class member and method.
There are three types of access modifiers:
- Public: the member can be accessed from anywhere.
- Private: the member can be access from within class only.
- Protected: the member can be accessed from within the class and subclasses.

4.2.1 Public Member


Python supports the notion of encapsulation through naming conventions. If the
identifier given to a class, or to a method or attribute of a class, begins with an
alphabetic character (e.g., color, getColor) that element is presumed to be of public
interest. For example:
class Car:
def __init__(self, make, model, year):
# Public attributes
[Link] = make
[Link] = model
[Link] = year
# Public method
def display_info(self):
print(f"{[Link]} {[Link]} {[Link]}")

# Creating an instance of the Car class


my_car = Car("Toyota", "Camry", 2022)
print(f"Make: {my_car.make}")# Accessing public
attributes
print(f"Model: {my_car.model}")
my_car.display_info()# Calling a public method

2
Object Oriented Programming I Chapter Four

So, all the attributes (make, model, year) and the method (display_info()) are
public since they can be accessed from anywhere.

4.2.2 Private Member


In Python, to designate something as private, we choose an identifier that begins
with double underscore characters (e.g., the __channel attribute of a Television).
This designation of privacy is supported by Python in several ways. When methods
of a class are named with a leading underscore, they are not displayed in
documentation generated by the help command or the pydoc utility. This keeps a
reader’s attention focused on the public aspects of the software. Furthermore, when
the wildcard form of an import is performed, only the public elements of the
module are loaded. Although these features help shield a user from knowledge of
the private details, Python does not strictly enforce the notion of privacy.
In order to protect the integrity of an object’s state, we generally encapsulate all
attributes of a class as private, preferring public access through designated
accessors and mutators. Since we allow the user to set arbitrary values, we might
have chosen a design with public data members that are directly manipulated. Yet
we still prefer to use encapsulation, as the required use of method calls affords us
some notion of control and monitoring.
Just as attributes can be designated as private by naming them with a leading
underscore, we can designate certain methods as private. While the public methods
are ones that we expect to be called by others, private methods are used for our
own convenience when implementing a class; they should only be called from
within the remainder of the class. For example:
class Television:
def __init__(self):
# Private attributes
self.__volume = 10 # Volume range is 0 - 100
self.__channel = 1 # Channel range is 1 - 100
self.__previous_channel = 1

# Public methods (accessors and mutators)


def set_volume(self, volume):
"""Set the volume of the TV, ensuring it's
within a valid range."""

3
Object Oriented Programming I Chapter Four

if 0 <= volume <= 100:


self.__volume = volume
else:
print("Volume must be between 0 and 100.")

def get_volume(self):
"""Return the current volume of the TV."""
return self.__volume

def set_channel(self, channel):


"""Set the TV channel, keeping track of the
previous channel."""
if 1 <= channel <= 100:
self.__previous_channel = self.__channel
self.__channel = channel
else:
print("Channel must be between 1 and 100.")

def get_channel(self):
"""Return the current channel."""
return self.__channel

def switch_to_previous_channel(self):
"""Switch back to the previous channel."""
self.__channel, self.__previous_channel =
self.__previous_channel, self.__channel
print(f"Switched to previous channel:
{self.__channel}")

# Usage Example
tv = Television()
tv.set_volume(30)
print(tv.get_volume()) # Output: 30

tv.set_channel(5)
print(tv.get_channel()) # Output: 5

tv.switch_to_previous_channel() # Output: Switched to


previous channel: 1
print(tv.get_channel()) # Output: 1

4
Object Oriented Programming I Chapter Four

Another example to explain the private member:

class PassKey:
""" A not-at-all secure way to store a secret
string. """
def __init__(self, plain_string, pass_phrase):
self.__plain_string = plain_string
self.__pass_phrase = pass_phrase

def decrypt(self, pass_phrase):


""" Only show the string if the pass_phrase is
correct."""
if pass_phrase == self.__pass_phrase:
return self.__plain_string
else:
return ""

4.2.3 Protected Member


A protected member can be accessed within a class and subclass. The protected
access modifier supports the inheritance. The protected access modifier supports
balance between private and public modifiers. Protected modifier provides the
same level of visibility to the base and subclass. To define the protected modifier,
use a single underscore as a prefix to the name of the member. For example:
class Car:
def __init__(self):
[Link] = ""
self._model = ""
self._year = 2024

def set_make(self, make):


[Link] = make

def set_model(self, model):


self._model = model

my_car = Car()
my_car.set_make("Toyota")
my_car.set_model("Camry")
print(my_car.make,my_car._model)
5
Object Oriented Programming I Chapter Four

4.3 @property Decorator for Encapsulation


Sometimes we use a method to generate a property of an object dynamically,
calculating it from the object’s other properties. Sometimes you can simply use a
method to access a single attribute and return it. You can also use a different
method to update the value of the attribute instead of accessing it directly. Methods
like this are called getters and setters, because they “get” and “set” the values of
attributes, respectively. In some languages you are encouraged to use getters and
setters for all attributes, and never to access their values directly and there are
language features which can make attributes inaccessible except through setters
and getters. In Python, accessing simple attributes directly is perfectly acceptable,
and writing getters and setters for all of them is considered unnecessarily verbose.
Setters can be inconvenient because they don’t allow use of compound assignment
operators. For example:
class Person:
def __init__(self, height):
[Link] = height
def get_height(self):
return [Link]
def set_height(self, height):
[Link] = height
Mohammed = Person(153) # Mohammed is 153cm tall
print([Link]) # Access attribute height getter
Mohammed.set_height(Mohammed.get_height()) # Getter
[Link] += 1 # Set height
Mohammed.set_height([Link] + 1) # Setter
As we can see, incrementing the height attribute through a setter is much more
verbose. Of course, we could write a second setter which increments the attribute
by the given parameter, but we would have to do something similar for every
attribute and every kind of modification that we want to perform. We would have a
similar issue with in-place modifications, like adding values to lists. Something
which is often considered an advantage of setters and getters is that we can change
the way that an attribute is generated inside the object without affecting any code
which uses the object.
For example, suppose that we initially created a Person class which has a fullname
attribute, but later we want to change the class to have separate name and surname

6
Object Oriented Programming I Chapter Four

attributes which we combine to create a full name. If we always access the


fullname attribute through a setter, we can just rewrite the setter, none of the code
which calls the setter will have to be changed. But what if our code accesses the
fullname attribute directly? We can write a fullname method which returns the
right value, but a method has to be called. Fortunately, the @property decorator
lets us make a method behave like an attribute:
class Person:
def __init__(self, name, surname):
[Link] = name
[Link] = surname

@property
def fullname(self):
return [Link] +" "+ [Link]

Mohammed = Person("Mohammed", "Kamil")


print([Link]) # no brackets!

There are also decorators which we can use to define a setter and a deleter for our
attribute (a deleter will delete the attribute from our object). The getter, setter and
deleter methods must all have the same name:
class Person:
def __init__(self, name, surname):
[Link] = name
[Link] = surname

@property
def fullname(self):
return [Link]+" "+[Link]

@[Link]
def fullname(self, fullname):
name, surname = [Link](' ', 1)
[Link] = name
[Link] = surname

7
Object Oriented Programming I Chapter Four

@[Link]
def fullname(self):
del [Link]
del [Link]

Ali = Person("Ali", "Samir")


print([Link])
[Link] = "Ali Sami"
print([Link])
print([Link])
print([Link])

4.4 Use Cases


This section list use cases that explain the encapsulation concept:

4.4.1 Restricting Direct Access


Encapsulation allows you to control access to certain data, ensuring that only
certain parts of the program can modify or access specific attributes. For example:
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute

def deposit(self, amount):


if amount > 0:
self.__balance += amount

def withdraw(self, amount):


if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Invalid withdrawal amount")

def get_balance(self):
return self.__balance

account = BankAccount(1000)
[Link](500)

8
Object Oriented Programming I Chapter Four

print(account.get_balance()) # Output: 1500


account.__balance = 10000 # This will not work because
__balance is private
print(account.get_balance())

Another example:
class Person:
def __init__(self, first_name, age):
self.first_name = first_name
self.__age = age

def show_age(self):
return self.__get_age()

def __get_age(self):
return self.__age

tk = Person('TK', 25)
print(tk.show_age()) # => 25
print(tk.__get_age())# AttributeError: 'Person' object
has no attribute '__get_age'

4.4.2 Data Validation


Encapsulation ensures that data assigned to class attributes is validated before
being set, improving data integrity. For example:
class Employee:
def __init__(self, salary):
self.__salary = salary

@property
def salary(self):
return self.__salary

@[Link]
def salary(self, value):
if value < 0:
print ("Error in input data!")

9
Object Oriented Programming I Chapter Four

else:
self.__salary = value

employee = Employee(5000)
print([Link]) # Output: 5000

[Link] = 7000 # Valid value


print([Link]) # Output: 7000
[Link] = -3000 # Raises ValueError

4.4.3 Restrict Access to Data


Using encapsulation, you can create attributes that are read-only, meaning they can
be accessed but not modified directly. For example:
class Product:
def __init__(self, name, price):
self.__name = name
self.__price = price
@property
def name(self):
return self.__name

@property
def price(self):
return self.__price

product = Product("Laptop", 1000)


print([Link]) # Output: Laptop
print([Link]) # Output: 1000
[Link] = 1200 # Raises AttributeError because
there's no setter

4.4.4 Restricting Access to Sensitive Data


Encapsulation helps protect sensitive information like passwords or account details
by making them private and only accessible via controlled methods. For example:
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute
10
Object Oriented Programming I Chapter Four

def get_balance(self):
return self.__balance

def deposit(self, amount):


if amount > 0:
self.__balance += amount
else:
print("Invalid deposit amount")

def withdraw(self, amount):


if amount <= self.__balance:
self.__balance -= amount
else:
print("Insufficient funds")

account = BankAccount(1000)
[Link](500)
print(account.get_balance()) # Output: 1500

[Link](200)
print(account.get_balance()) # Output: 1300

11

You might also like