Object Oriented Programming (OOP) with examples in Python
This post explains the concepts of object-oriented programming (OOP) and its key features, including encapsulation, abstraction, inheritance, and polymorphism. It provides examples of how these concepts can be implemented using Python, such as using private and protected variables, defining abstract classes and interfaces, creating subclasses, and implementing method overriding.
Introduction
Object-oriented programming (OOP) is a programming paradigm that organizes code into objects, which are instances of classes that contain data (attributes) and behavior (methods). OOP is based on the concept of “objects,” which represent real-world entities or concepts.
In OOP, a class is a blueprint or template for creating objects. A class defines the properties and methods that an object will have, and each object created from a class will have its own set of data and behavior. OOP is a powerful and flexible programming paradigm that allows developers to create complex, modular, and scalable applications.
Many programming languages support object-oriented programming (OOP) concepts. Some of the most popular ones are:
- Java: Java is a popular OOP language that was designed to be simple, secure, and platform-independent.
- Python: Python is another popular OOP language that is known for its simplicity and ease of use.
- C++: C++ is an OOP language that is often used for building large-scale applications and systems. Other languages are Swift, JavaScript, Ruby, PHP, etc.
Key Features
Some key features of OOP include:
- Encapsulation: This is the process of hiding implementation details and exposing only the necessary information or functionality to the user. Encapsulation is achieved by defining public and private methods and variables within a class.
- Abstraction: This is the process of simplifying complex systems by breaking them down into smaller, more manageable parts. In OOP, abstraction is achieved by creating abstract classes or interfaces that define common functionality or behavior, which can then be implemented by other classes.
- Inheritance: This is the process of creating a new class from an existing class, inheriting its properties and methods. The new class can add or modify functionality as needed, while still maintaining the original properties and methods of the parent class.
- Polymorphism: This is the ability of objects to take on many forms or behaviors. In OOP, polymorphism is achieved through method overriding and method overloading, which allow methods to have different implementations depending on the specific object or context.
Encapsulation
Encapsulation is the process of hiding implementation details and exposing only the necessary information or functionality to the user. In Python, encapsulation can be achieved using the following methods:
- Private variables and methods: In Python, private variables and methods can be defined by using the double underscore (
__
) prefix. This makes the variable or method inaccessible outside the class. For example:
class MyClass:
def __init__(self):
self.__my_var = 10
def __my_method(self):
print("This is a private method")
my_obj = MyClass()
print(my_obj.__my_var) # This will raise an AttributeError
my_obj.__my_method() # This will also raise an AttributeError
- Protected variables and methods: In Python, protected variables and methods can be defined by using the single underscore (
_
) prefix. This makes the variable or method accessible only within the class and its subclasses. For example:
class MyClass:
def __init__(self):
self._my_var = 10
def _my_method(self):
print("This is a protected method")
class MySubclass(MyClass):
def __init__(self):
super().__init__()
def print_var(self):
print(self._my_var) # This is accessible from the subclass
my_obj = MyClass()
print(my_obj._my_var) # This is accessible from outside the class, but it's not recommended
my_obj._my_method() # This is accessible from outside the class, but it's not recommended
my_subobj = MySubclass()
my_subobj.print_var() # This will print 10
- Property methods: In Python, property methods can be used to control access to variables and provide validation or other custom behavior. Property methods can be defined using the
@property
decorator and the setter method. The@property
decorator is used to define a method as a “getter” for a class attribute. When this method is called, it returns the value of the attribute. For example:
class MyClass:
def __init__(self):
self._my_var = 10
@property
def my_var(self):
return self._my_var
@my_var.setter
def my_var(self, value):
if value > 0:
self._my_var = value
my_obj = MyClass()
print(my_obj.my_var) # This will print 10
my_obj.my_var = 20
print(my_obj.my_var) # This will print 20
my_obj.my_var = -5 # This will not change the value of my_var because of the validation defined in the setter method
print(my_obj.my_var) # This will still print 20
In Python, a property is a special kind of method that allows you to control access to an object’s attributes. Property methods are useful because they allow you to add custom logic to the act of setting or retrieving an object’s attributes, without requiring users of your object to change their code. Let us see another example:
class MyClass:
def __init__(self):
self._x = None
@property
def x(self):
print("Getting x")
return self._x
@x.setter
def x(self, value):
print("Setting x")
self._x = value
@x.deleter
def x(self):
print("Deleting x")
del self._x
obj = MyClass()
obj.x = 10 # This calls the setter method
print(obj.x) # This calls the getter method
# Delete the value of x
del obj.x
# Output: Deleting x
# Try to get the value of x again
print(obj.x)
# Output: Getting x
# Output: None
In this example, x
is a property of the MyClass
object. The @property
decorator is used to define a method called x
that serves as the getter for the x
property. This method is called whenever the x
property is accessed. In addition, a method called x.setter
is defined to set the value of the x
property. When the x
property is set, this method is called.
The @property
decorator can be used without the setter method, in which case the property becomes read-only. In addition, it can be used with the deleter method, which allows you to define what happens when the property is deleted. Deleter methods can be useful when you need to perform additional actions when a property is deleted, such as logging, cleaning up resources, or invalidating related properties.
In short, encapsulation in Python is achieved through the use of private and protected variables and methods, as well as property methods to control access to variables and provide validation or other custom behavior.
Abstraction
Abstraction is a key concept in object-oriented programming that allows complex systems to be designed and implemented in a more manageable way. Abstraction involves creating a simplified, abstract representation of a complex system that hides its implementation details and exposes only the essential features that are needed for a particular purpose.
In Python, abstraction is achieved through the use of abstract classes and interfaces. An abstract class is a class that cannot be instantiated directly, but can only be used as a superclass for other classes. An abstract class may define one or more abstract methods, which are methods that are declared but not implemented. Abstract methods serve as placeholders for methods that will be implemented in subclasses, and they provide a way to ensure that subclasses provide their own implementation of the method.
Here’s an example of how abstraction can be achieved in Python using an abstract class:
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def start(self):
raise NotImplementedError("Subclass must implement abstract method")
def stop(self):
raise NotImplementedError("Subclass must implement abstract method")
In this example, we define a Car
class that has three attributes: make
, model
, and year
. We also define two methods, start
and stop
, which raise a NotImplementedError
exception. These methods are considered “abstract” because they don’t have any implementation details yet.
Now, let’s create a subclass of Car
that implements the abstract methods:
class ElectricCar(Car):
def __init__(self, make, model, year, battery_size):
super().__init__(make, model, year)
self.battery_size = battery_size
def start(self):
print("Starting the electric car")
def stop(self):
print("Stopping the electric car")
my_car = ElectricCar("Tesla", "Model S", 2021, 100)
my_car.start() # Output: Starting the electric car
my_car.stop() # Output: Stopping the electric car
In this example, we create a subclass of Car
called ElectricCar
. We define the same three attributes as in the parent class, and we also add a battery_size
attribute. We then implement the abstract start
and stop
methods with some implementation details specific to an electric car.
Here, we create an instance of ElectricCar
and call its start
and stop
methods. We don’t need to know the details of how these methods are implemented, as they are abstracted away by the Car
class.
Another way to perform abstraction in Python is to use the “Abstract Base Classes” module. It is a built-in module that provides a way to define abstract classes and abstract methods in Python.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
# Create instances of Rectangle and Circle
rect = Rectangle(5, 6)
circ = Circle(3)
# Call the area() method on each instance
print(rect.area()) # Output: 30
print(circ.area()) # Output: 28.26
In this example, we define an abstract class Shape
that has an abstract method area
. We then define two concrete subclasses of Shape
, Rectangle
and Circle
, that implement the area method with their own implementation details.
The abc
library provides a way to enforce the use of abstract classes and methods in the code. When we define an abstract method using the @abstractmethod
decorator, any concrete subclass that fails to implement that method will raise a TypeError
exception when an attempt is made to instantiate the subclass. This ensures that all concrete subclasses of an abstract class provide an implementation for the abstract method.
Inheritance
Inheritance is a feature of object-oriented programming that allows a new class to be based on an existing class, inheriting all its properties and methods. The existing class is known as the parent class or superclass, and the new class is known as the child class or subclass.
In Python, inheritance is achieved by creating a subclass that inherits the properties and methods of the superclass. The syntax for creating a subclass is as follows:
class Subclass(Superclass):
pass
This creates a new class Subclass that inherits from the Superclass. The pass keyword is used here to indicate that the subclass has no additional properties or methods.
The subclass can then override or extend the properties and methods of the superclass, or add new ones. For example:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("I am an animal")
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name)
self.color = color
def speak(self):
print("Meow")
def catch_mouse(self):
print("I caught a mouse!")
my_animal = Animal("Leo")
my_animal.speak() # This will print "I am an animal"
my_cat = Cat("Whiskers", "gray")
my_cat.speak() # This will print "Meow"
my_cat.catch_mouse() # This will print "I caught a mouse!"
print(my_cat.name) # This will print "Whiskers"
print(my_cat.color) # This will print "gray"
In this example, Cat
is a subclass of Animal
, and it overrides the speak method to print “Meow” instead of “I am an animal”. It also adds a new method catch_mouse
that is specific to cats. The __init__
method of Cat
calls the __init__
method of Animal
using the super()
function to initialize the name property inherited from Animal
.
In short, inheritance in Python allows for code reusability and makes it easier to create new classes that share properties and methods with existing ones.
Polymorphism
Polymorphism is a feature of object-oriented programming that allows objects of different classes to be treated as if they are of the same class. This means that you can use a single interface to represent multiple classes, and you can write code that can work with objects of different classes without needing to know their specific types.
In Python, polymorphism is achieved through the use of duck typing and method overriding. Duck typing means that if an object walks like a duck and quacks like a duck, then it is treated as a duck. This means that if two objects have the same methods or attributes, they can be treated as if they are the same type of object.
Method overriding is the ability of a subclass to provide its own implementation of a method that is already defined in its parent class. When a method is called on an object of the subclass, the implementation in the subclass is used instead of the one in the parent class.
Here’s an example of how polymorphism can be achieved in Python:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
def animal_speak(animal):
print(animal.speak())
my_dog = Dog("Fido")
my_cat = Cat("Whiskers")
animal_speak(my_dog) # This will print "Woof!"
animal_speak(my_cat) # This will print "Meow!"
In this example, Animal
is a superclass that defines a method speak
, which does nothing. Dog
and Cat
are subclasses that inherit from Animal
and override the speak
method to return “Woof!” and “Meow!”, respectively.
The animal_speak
function takes an object of type Animal
as an argument, and calls its speak
method. Since Dog
and Cat
are both subclasses of Animal
and have overridden the speak
method, they can be passed as arguments to animal_speak
, and their speak
method will be called accordingly.
In short, polymorphism in Python allows for code that is more flexible, reusable, and easier to maintain, since it can work with objects of different types without needing to know their specific types.
Disclaimer of liability
The information provided by the Earth Inversion is made available for educational purposes only.
Whilst we endeavor to keep the information up-to-date and correct. Earth Inversion makes no representations or warranties of any kind, express or implied about the completeness, accuracy, reliability, suitability or availability with respect to the website or the information, products, services or related graphics content on the website for any purpose.
UNDER NO CIRCUMSTANCE SHALL WE HAVE ANY LIABILITY TO YOU FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF THE SITE OR RELIANCE ON ANY INFORMATION PROVIDED ON THE SITE. ANY RELIANCE YOU PLACED ON SUCH MATERIAL IS THEREFORE STRICTLY AT YOUR OWN RISK.
Leave a comment