A Comprehensive Guide to Understanding Object-Oriented Programming (OOP)  in  Python

A Comprehensive Guide to Understanding Object-Oriented Programming (OOP) in Python

Exploring the Key Concepts of Object Oriented Programming in Python

In this article we'll cover in-depth about Object Oriented Programming (OOP) in Python programming. OOP concepts are pretty same for most programming languages but their syntax and implementation approach varies.

💡
Already familiar with OOP in Python or New to Python? Check out the previous comprehensive article on Python Functions titled: Efficiency and Reusability : Harnessing the Power of Functions in Python in this Python series.

Brief information on OOP will be covered in this article, there are about 11 comprehensive and profound articles i published in my blog previously, were i explained OOP concepts extensively with profound examples in PHP, Dart and JavaScript.

Object Oriented Programming (OOP)

Object-oriented programming (OOP) is a programming style based on the idea of representing real life scenarios using "objects", with their unique or common description been represented as attributes and the set or defined way of their interaction or behavior referred to as methods.

Every standard attribute of object oriented programming languages are available in Python.

Class and Objects

A Class: a code template that is extensible to have more code templates inheriting it's member and behavior by providing their default or initial format of state(features or attributes) and actions(methods)

A car is a general name for a transport system with 4 tires which can be referred to as Car Class. Benz or Toyota is an object(example or instance) of this car class. Further more different models are instances or examples of Benz or Toyota which makes Toyota a class.

An Object: simply put, an object is an instance of a class. Objects themselves can become classes if instances are created from them. For example, the Toyota 4Runner can become an object of Toyota.

💡
Excerpt from my article Object Oriented Programming(OOP) Series: Classes and Objects check the link for detail explanation on Classes and Objects

Python Class

  • Structure of Class in Python

Class definition in Python starts with the reserved keyword class followed by the name of the class, then a colon before the body of the class.

class ClassName:
    <statement | expression -1>
    .
    .
    .
    <statement | expression - N >
    #(as much as the developer intends to write in the body of the code

NB: The pass keyword is used to skip the error that occurs when an empty class is defined because in Python class cannot be empty.

class Car:
    pass
  • Example of Class in Python

Attribute reference and instantiation are two operations are supported in Python. The value of a Class attribute can be altered by assigning a value to it depending on the access modifier definition.

class Car:
    #docstring is also a valid class attribute
    """A Car class in Python"""
    #class attribute brand
    brand = "Earth"

    # class function which in oop context is called method
    def horn(self):
        return 'method of car class'

In the example above, the name of the class is Car and it has attribute brand and method horn. This can be accessed by an instance (object) of the class with the dot . operator. The self parameter is similar to this in other programming language class definition. Any word can be used in place of self but it must be the first parameter. It basically points to the current instance of the class itself and can be used to access the attributes and methods. So what actually is Attribute and Method?

Attributes

Attributes are data members inside a class or an object that represent the different features of the class. They can also be referred to as characteristics of the class that can be accessed from other objects or differentiate a class from other classes. For example a car attribute include: tire, door, seats, license plate, headlight, wheel, handle, make, year, brand.

Methods

In classes methods are responsible to modify or define the behavior of the class and it's objects. A car method includes start, headLightOn, drive, stop, openDoor, horn etc. The methods in a class can either access(get or set) an attribute or perform a specific operation. It's a logic or procedure defined in a class to do something. Methods in classes lay out the behavior of objects that will be created. For instance, if a car owner presses the horn button it'll function by horning.

💡
For in-depth understanding on Attributes and Method checkout this article i published Object Oriented Programming(OOP) Series: Attributes and Methods where i pulled the above excerpt from.
  • __str__() method

class Car:
    """A Car class in Python"""
    def __init__(self, brand, location):
        self.brand = brand
        self.location = location

car1 = Car("Toyota", "Earth")
print(car1)

#<__main__.Car object at 0x000001781A752B90>

Without the __str__ function the class returns the object string. But when it is defined in the class it returns the string or response.


class Car:
    """A Car class in Python"""
    def __init__(self, brand, location):
        self.brand = brand
        self.location = location

    def __str__(self):
        return f"My car name is: {self.brand} and the location is: {self.location}"

car1 = Car("Toyota", "Earth")
print(car1)

#My car name is: Toyota and the location is: Earth

Objects in Python

An object like earlier mentioned is an instance of a class. For example, A Toyota(an object) is a combination of the attributes(members or variables), methods(behaviors or actions), style of arrangement in the class of Car.

To instantiate an object (copy of a class) from a class is like assigning a function to a variable.

class Car:
    #docstring is also a valid class attribute
    """A Car class in Python"""
    #class attribute brand
    brand = "Earth"

    # class function which in oop context is called method
    def horn(self):
        print(f"Accessing the attribute from the method named brand: {self.brand}")

toyota = Car()
print(toyota.brand)
toyota.horn()

#Earth
#Accessing the attribute from the method named brand: Earth

In this snippet above, the toyota is an instance (object) of the Car class.

toyota.brand is used to access the car class attribute.

toyota.horn() is used to access the car class method.

Delete Object Properties

Objects in Python can be deleted using the Python del keyword.

#delete property on an object
del toyota.brand

#delete objects 
del toyota

NB: variables within a method are unique to each instance created and the class attributes (variables) can be shared by all the instances of the class.

class Car:

    brand = 'Baja'         # class attribute will be shared by all instances of the class

    def __init__(self, product):
        self.product = product    # instance attribute unique to each instance of the class


car1 = Car('Toyota')
car2 = Car('Honda')
print(f"car1 brand is: {car1.brand} and car2 brand is: {car2.brand} and the instance attribute for {car1.product} is unique to {car2.product}")

#car1 brand is: Baja and car2 brand is: Baja and the instance attribute for Toyota is unique to Honda

Constructor in Python

When creating class there are certain attributes or methods that might be needed for the class to function as designed or efficiently. The method that is defined in a class using __init__ is an example of Python's constructor. It is automatically invoked and executed when an object is created from a class. It’s a unique member function in a class responsible for initializing the expected parameters or behavior of its objects upon creation (instantiation). It is unique because it can take the name of the class.

Types of Constructor: Default constructor, Parameterized constructor, Copy constructor, Overloaded constructor

Note: constructors are optional in a class and some object oriented languages can automatically create one for you when there is none.

💡
For in-depth understanding on Constructor and Destructor checkout this article i published Object Oriented Programming(OOP) Series: Constructor and Destructor where i pulled the above excerpt from.
  • Example of Python __init__ method (constructor)

Once the car1 object is instantiated the __init__ method gets invoked automatically.

 class Car:
    """A Car class in Python"""
    def __init__(self, brand, location):
        self.brand = brand
        self.location = location

car1 = Car("Toyota", "Earth")
print(f"My car name is: {car1.brand} and the location is: {car1.location}")

#My car name is: Toyota and the location is: Earth

Inheritance in Python

Inheritance is the mechanism in which a class inherit the attributes and methods of another class. The class which inherits from the other class is referred to as child class while the class whose methods and attributes were inherited is known as base class or parent class.

As the inheritance grows it begins to take a tree structure. The further it grows the deeper the tree structure. Hence the need for dependency injection.

Types of Inheritance: Single Inheritance, Multiple Inheritance, Multi-Level Inheritance.

💡
Excerpt from my article Object Oriented Programming(OOP) Series: Inheritance check the link for detail explanation on Inheritance.
  • Structure of Inheritance in Python
class DerivedClassName(BaseClassName):
    <statement | expression -1>
    .
    .
    .
    <statement | expression - N >
    #(as much as the developer intends to write in the body of the code
  • Python supports multiple inheritance
class DerivedClassName(BaseClassName1, BaseClassName2, BaseClassName3):

derived class is called child class while base class is called parent class.

  • Example of Inheritance
# Base (parent) class
class Car:

    def __init__(self, brand, year, price):
        self.brand = brand
        self.year = year
        self.price = price

    def details(self):
        print(self.brand, self.year, self.price)

# Derived (Child) class Mercedes 
class Mercedes(Car):

    def horn(self, status):
        print(f'The {self.brand} horn status is: {status}')


car = Mercedes('Mercedes-Benz G-Class', 'Dark Ash', 100000)
car.details()
car.horn(True)
car.horn(False)

The derived class can access the brand attribute and horn method of the parent class.

BaseClassName must to be defined in a namespace that is reachable from the scope that has the definition of the derived class. If the base class in defined in another module it can be accessed as shown below.

class DerivedClassName(modulename.BaseClassName):

Namespace

Namespace is the approach of grouping named scope to avoid conflicts with similar names as used in other scopes. Namespace block contains symbols that are grouped into a named scope to avoid conflicts with similarly named symbols in other scopes.

This is the meaning across several object-oriented languages but the implementation varies based on the syntax used.

💡
Excerpt from my article Object Oriented Programming(OOP) Series: Namespaces check the link for detail explanation on Inheritance.

According to Python documentation: A namespace is a mapping from names to objects. The important thing to know about namespaces is that there is absolutely no relation between names in different namespaces; for instance, two different modules may both define a function maximize without confusion — users of the modules must prefix it with the module name.

Encapsulation in Python

Encapsulation is used to hide sensitive part of a method or class to avoid unwanted access or modification. Data hiding is controlled using getter or setter methods for data attributes that can allow read or write access by other classes.

A getter method is used to read the value of a specific data attributes within a class. A setter method is used to write or modify the value of a specific data attribute within a class.

Access modifiers(specifiers) are keywords or terms that defines the accessibility of classes, methods, and attributes. Access modifiers varies according to the syntax used by OOP languages to enhance the security or coverage of data members and methods.

Object Oriented Programming(OOP) Series: Access modifiers (public, private, protected) are used to define the way this hidden data is to be accessed.

💡
Excerpt above is from my article Object Oriented Programming(OOP) Series: Encapsulation check the link for detail explanation on Inheritance.

In python, _attrName or __attrName is used to denote private attributes.

class Car:
    """A Car class in Python"""
    def __init__(self, brand, location):
        self.brand = brand
        self.location = location
        self.__config_rate = 5

    def __str__(self):
        return f"My car name is: {self.brand} and the location is: {self.location} with config rate of: {self.__config_rate}"

    def setConfigRate(self, rate):
        self.__config_rate = rate



car1 = Car("Toyota", "Earth")
print(car1)

car1.__config_rate = 10
print(car1)

# using the setter method to change the config rate
car1.setConfigRate(8)
print(car1)

The value of the __config_rate can only be changed using the setter method setConfigRate().

Polymorphism in Python

Polymorphic system (polymorphism) indicates the presence of an interface with multiple variations existing in one or more stages or forms of it's trait.

NB: Python supports method overriding (when same method name of the parent class is been used and modified in the child class) but does not support method overloading.

💡
Excerpt above is from my article Object Oriented Programming(OOP) Series: Polymorphism check the link for detail explanation on Inheritance.
class Car:
    def info(self):
        pass

class Toyota(Car):
    def info(self, brand):
        print(f"This car brand is: {brand}")

class Mercedes(Car):
    def info(self, brand):
        print(f"This car brand is: {brand}")


car1 = Toyota()
car1.info("Toyota Corolla")

car2 = Mercedes()
car2.info("Mercedes-Benz G-Class")

The info method can perform different functionalities in different instances.

Data Abstraction in Python

An abstract class exposes functionality that are important for implementation. An abstract class is a class that cannot be instantiated directly except by a class that extends it to an object.

An abstract method is a method without implementation.

💡
Excerpt above is from my article Object Oriented Programming(OOP) Series: Abstraction check the link for detail explanation on data abstraction.

Abstract classes and interfaces in Python can be used to define data abstraction. An interface is an abstract class with defined methods but no implementations for the use of the methods.

Let's look at an example of an abstract class:

from abc import ABC, abstractmethod

class Car(ABC):
    @abstractmethod
    def info(self):
        pass

    @abstractmethod
    def speed(self):
        pass


class Toyota(Car):
    def __init__(self, brand, location, color):
        self.brand = brand
        self.location = location
        self.color = color

    def info(self):
        return f"My car name is: {self.brand} and the location is: {self.location}"

    def speed(self):
        return f"My car speed is 1000 with color: {self.color}"


car1 = Toyota('Mercedes-Benz G-Class', "Earth", "Dark Ash")
print(car1.info())
print(car1.speed())

#output
#My car name is: Mercedes-Benz G-Class and the location is: Earth
#My car speed is 1000 with color: Dark Ash

the ABC, abstractmethod is imported from abc module. The Car class has two abstract methods info() and speed() which got implemented by the Toyota subclass before the instance of the Toyota class is created.

💡
The best way to master these concepts is by practicing or by studying (setting up, debugging, extending the features etc.) a Python codebase from public repository. You can read more in Python documentation there exist other examples and implementations of object oriented programming concepts.

Conclusion

In this article, we've covered in-depth on Object oriented programming (OOP). OOP is based on the idea of using "objects" to represent real-world scenarios. Each object's distinct or common description is represented by an attribute, and each method is a predetermined way for an object to interact or behave. OOP makes the code clean, structured, scalable and inheritable. Real life applications can be developed using the OOP approach. Python is an OOP language and can be used to develop applications that solve real-world problems. A class can be reused within or in other files as modules.

Find this helpful or resourceful?? kindly share and feel free to use the comment section for questions, answers, and contributions.

💡
Follow me on Hashnode: Alemsbaja X: Alemsbaja | Youtube: Tech with Alemsbaja to stay updated on more articles

Did you find this article valuable?

Support Alemoh Rapheal Baja by becoming a sponsor. Any amount is appreciated!

Â