The Ultimate Guide to Object-Oriented Programming in Python
Khumbo Klein Chilamwa
Posted on July 11, 2023
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
- 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
- Objects -These are instances of a class created with specifically defined data
- Methods -These are functions defined inside a class describing the behavior of objects
- 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
Example1: Creating a Car class
class Car:
pass
If you do print(type(Car))
you will get the below output:
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()
Example2: Creating a class object
audi = Car()
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)
Output:
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())
Output:
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
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)
Output:
●__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)
Output:
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:
# 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)
Output:
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)
Output:
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)
- 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)
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
Output:
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
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)
Output:
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
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)
Output:
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())
Output:
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.
Posted on July 11, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.