The Ultimate Guide to Object-Oriented Programming in Python

khumbolamulungu

Khumbo Klein Chilamwa

Posted on July 11, 2023

The Ultimate Guide to Object-Oriented Programming in Python

Object-Oriented Programming, also known as OOP, is one of those important topics that any programmer needs to have full knowledge of. Before diving any deeper into this topic, let us define what OOP is in general and why it is a must-have skill for any programmer. OOP in Python is a method of structuring a program by bundling related properties and behaviors into individual objects.

Why OOP?

Object Oriented Programming comes with so many benefits for the programmer, some of these benefits are listed below:

  • Makes your code reusable
  • Makes your code clean and gives it a clear structure
  • Makes your code easy to maintain

The structure of OOP

  1. Classes -These are user-defined data types that act as a blueprint for objects, methods, and attributes or they are containers of objects, methods, and attributes
  2. Objects -These are instances of a class created with specifically defined data
  3. Methods -These are functions defined inside a class describing the behavior of objects
  4. Attributes -These represent the state of objects in a class

Creating our first class

We can create a class using the class keyword followed by the class name and a colon at the end.
Syntax:

class ClassName:
    statement1
        .
        .
    statementN
Enter fullscreen mode Exit fullscreen mode

Example1: Creating a Car class

class Car:
    pass
Enter fullscreen mode Exit fullscreen mode

If you do print(type(Car)) you will get the below output:

Image description

Class objects

An object is an instance of a class and it must be known that we can have as many objects as possible from a class. The process of creating a class object is also called object instantiating, you can only create an object for a class that exists.
Syntax:

object_name = ClassName()
Enter fullscreen mode Exit fullscreen mode

Example2: Creating a class object

audi = Car()
Enter fullscreen mode Exit fullscreen mode

audi is an object for the class Car

Creating a class and its objects

# creating a class Car
class Car:
    pass
# instantiating the Car class
car1 = Car()
car2 = Car()
# creating attributes for instances car1 and car2
car1.name = "Audi A7"
car2.name = "Nissan Demio"
car1.year_made = "2000"
car2.year_made = "1994"
# printing the output
print(car1.name, "was built in", car1.year_made)
print(car2.name, "was built in", car2.year_made)
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Methods

These are functions defined inside a class describing the behavior of objects tied to that class. Methods inside a class will always take self as the first parameter.
Example3: Creating methods inside a class

# creating a class Car
class Car:
    # initializing method(one of the special methods)
    # called everytime an instance is created
    # takes self always
    def __init__(self, name, year_made):
        self.name = name
        self.year_made = year_made
    # normal method to return the car info   
    def carInformation(self):
        return self.name + " was built in " + self.year_made

#instantiating class objects 
car1 = Car("Audi A7", "2000")
car2 = Car("Nissan Demio", "1994")
# calling the carInformation() method
print(car1.carInformation())
print(car2.carInformation())
Enter fullscreen mode Exit fullscreen mode

Output:

Image description
Note:
Any object has access to everything contained in the class, for example, object car1 has access to method carInformation().

Let us now talk about the __init__ or dunder init method also known as the constructor, this method is used to initialize an instance, and the name is fixed (cannot be renamed to anything). It is inside the __init__ method that we define or instantiate the class attributes, apart from attributes self is another parameter that __init__ method takes.

def __init__(self, name, year_made):
    self.name = name
    self.year_made = year_made
Enter fullscreen mode Exit fullscreen mode

Other special methods used in classes

__str__ is used for displaying data in a beautiful way
Example:

# creating a class Car
class Car:
    def __init__(self, name, year_made):
        self.name = name
        self.year_made = year_made
    #the __str__ function for display pretty output 
    def __str__(self):
        return self.name
# instantiating an object
car1 = Car("Range Rover", "2019")
# calling the whole object
print(car1)
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

__repr__ is used for logging and debugging
Example:

# creating a class Car
class Car:
    def __init__(self, name, year_made):
        self.name = name
        self.year_made = year_made
    #the __repr__ function for display pretty output 
    def __repr__(self):
        return self.name
# instantiating an object
car1 = Car("Range Rover", "2019")
# calling the whole object
print(car1)
Enter fullscreen mode Exit fullscreen mode

Output:
Image description
Note:
The __str__ and __repr__ do not differ much in nature

Class and instance variables

As you are creating your classes you might want to work with class variables or instance variables or work with both, so how do these two differ then? The below table explains everything:

Image description
Example4: Class variables

# creating a class Car
class Car:
    # creating the class variable after class definition
    maximum_speed = "210 km/hr"
    # initializing method(one of the special methods)
    # called everytime an instance is created
    # takes self always
    def __init__(self, name, year_made):
        self.name = name
        self.year_made = year_made
    # normal method to return the car info   
    def carInformation(self):
        return self.name + " was built in " + self.year_made

#instantiating class objects 
car1 = Car("Audi A7", "2000")
# calling the carInformation() method
print(car1.carInformation())
# accessing class and instance variable using the object
print(car1.name, "has maximum speed of", car1.maximum_speed)
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Example5: Instance variables

# creating the class
class Car:
    # initializing method
    def __init__(self, name, year_made):
        # 2 instance variables name and year_made
        self.name = name
        self.year_made = year_made
#instantiating the class object   
car1 = Car("Audi A7", "2000")
# car1.name and car1.year_made accesses instance variables
print("Car name:", car1.name)
print("Year made:", car1.year_made)
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Inheritance

Inheritance in Object Oriented Programming is a concept where a new class is created using an already existing class. As you are inheriting from a class, it means you are getting the methods, and attributes of that class, in short, you are getting everything contained in that class.

Why inheritance?

Inheritance allows the reusability of code, this means that we do not have to write the same code over and over again, and we can easily reuse a method with the same or modified data.

Types of inheritance

  • Single inheritance - a child class inherits from only one parent class Syntax:
class ParentClass:
    statement(s)

class ChildClass(ParentClass):
    statement(s)
Enter fullscreen mode Exit fullscreen mode
  • Multiple inheritance - a child class inherits from multiple parent classes Syntax:
class ParentClass:
    statement(s)

class AnotherParentClass:
    statement(s)

class ChildClass(ParentClass, AnotherParentClass):
    statement(s)
Enter fullscreen mode Exit fullscreen mode

Example1: Using inheritance

class Car:
    # creating class variables
    maximum_speed = "210 km/hr"
    number_of_tyres = 4
    # the dunder init method/initializer
    def __init__(self, name, year_made):
        self.name = name
        self.year_made = year_made

# class Minibus inheriting from class Car
class Minibus(Car):
    # __init__ method of class Minibus
    def __init__(self, name, year_made, price):
        self.name = name
        self.year_made = year_made
        self.price = price
    # method to return price
    def carPrice(self):
        return self.price
# instantiating the object for class Minibus
minbus1 = Minibus("Vanetti", "2013", "MWK4500000")
# printing data
print("Name:", minbus1.name)#name for Minibus class
print("Year made:", minbus1.year_made)#year for Minibus class
print("Maximum speeed:", minbus1.maximum_speed)#max_speed inherited from Car
print("Number of tyres:", minbus1.number_of_tyres)#number_of_tyres inherited from Car
print("Price:", minbus1.carPrice())#calling the carPrice() method in Minibus
Enter fullscreen mode Exit fullscreen mode

Output:

Image description

Encapsulation

As you are writing your programs in an Object Oriented Programming way, situations arise where you want your class data not to be modified in any way, to solve this encapsulation comes into play. Encapsulation is defined as a way of restricting access to methods and variables available in a class. Using encapsulation, you can make your class attributes private or protected.

  • Private - These attributes are invisible and inaccessible from outside the class itself

Syntax:

def __init__(self, name, year_made):
    self.__year_made = year_made#this attribute is private
Enter fullscreen mode Exit fullscreen mode

We use double underscores __ to denote a private attribute

Example2: Creating a private attribute

# creating our class Car
class Car:
    # initializing attribute name via __init__
    def __init__(self, name):
        self.__name = name#this is a private attribute

#instantiating an object   
car1 = Car("BMW")
# printing the private attribute
print(car1.name)
Enter fullscreen mode Exit fullscreen mode

Output:

Image description
We are getting an error because the private attribute is invisible and inaccessible outside the class

  • Protected - This attribute can only be accessed via a subclass but it’s possible to access them outside the class

Syntax:

def __init__(self, name, year_made):
    self._name = name#this attribute is protected
Enter fullscreen mode Exit fullscreen mode

We use a single underscore _ to denote a protected attribute
Example3: creating a protected attribute

# creating our class Car
class Car:
    # initializing attribute name via __init__
    def __init__(self, name):
        self._name = name#this is a protected attribute

#instantiating an object   
car1 = Car("BMW")
# printing the protected attribute
print(car1._name)
Enter fullscreen mode Exit fullscreen mode

Output:

Image description
To access the protected attribute outside the class, we use a single underscore before the attribute name e.g. car1._name

Note:
Attributes without any underscores are said to be public, this means they are visible and accessible even from outside the class itself using an object.

Polymorphism

Poly means many and morphism means forms, so polymorphism means having many forms. In Python, polymorphism means the same function name is used in different ways.
Example4: Using polymorphism

# creating class Square
class Square:
    # initializer
    def __init__(self, side):
        self.side = side 
    # common method
    def calculate_area(self):
        return self.side * self.side
# creating class Triangle
class Triangle:
    # initializer
    def __init__(self, base, height):
        self.base = base
        self.height = height
    # common method
    def calculate_area(self):
        return 0.5 * self.base * self.height
# instantiating class objects
sq = Square(6)
tri = Triangle(2, 7)
# printing results
print("Area of square: ", sq.calculate_area())
print("Area of triangle: ", tri.calculate_area())
Enter fullscreen mode Exit fullscreen mode

Output:

Image description
In our two classes, we have created a common method calculate_area() that is being used in different ways, that is calculating the area of a square and a triangle.

Conclusion

In summary, mastering object-oriented concepts is a must-have skill for any developer. Following an object-oriented paradigm when writing your code makes it cleaner, maintainable, and reusable.

💖 💪 🙅 🚩
khumbolamulungu
Khumbo Klein Chilamwa

Posted on July 11, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related