Object-Oriented Programming in Python - Fundamentals

Object-Oriented Programming in Python - Fundamentals

What is Object-Oriented Programming?

Python is an object-oriented programming language. Object-oriented programming is a programming paradigm that structures programs such that data and functions are bundled into individual objects. For instance, an object could represent a car with data like a type, color, and model and functions such as driving, parked, neutral, and reversing. Objects are at the center of object-oriented programming in Python, not only representing the data but in the overall structure of the program as well.

Object-Oriented Programming concepts

In this article, we will be discussing the following OOP concepts:

  • Class and Objects
  • Inheritance
  • Overriding parent method
  • Encapsulation
  • Polymorphism

Classes and Objects

An object is an entity that has data(variables) and functions(methods) associated with it. A class is like an object constructor or a "blueprint" for creating objects. The class defines attributes and the behavior of the object, while the object, on the other hand, represents the class.

conf.gif Confusing right? don't worry you will understand better with a few examples.

But first, let us look at some inbuilt classes in python:

C:\Users\ODION>python
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> num = 5
>>> type(num)
<class 'int'>
>>> string = 'string'
>>> type(string)
<class 'str'>
>>> lst = []
>>> type(lst)
<class 'list'>
>>> tpl = ()
>>> type(tpl)
<class 'tuple'>
>>> sets = set() 
>>> type(sets)
<class 'set'>
>>> dct = {}
>>> type(dct)
<class 'dict'>
>>> boolean = True
>>> type(boolean)
<class 'bool'>

Creating a Class

To create a class we need to use the keyword class followed by the name and colon. The class name should be CamelCase.

# syntax
class ClassName:
  '''code goes here'''

Example:

class Person:
  pass
print(Person)

Output:

<class '__main__.Person'>

Creating an Object

We can create an object by calling the class.

obj = Person()
print(obj)

Output:

<__main__.Person object at 0x000001FD94D7A680>

Here obj is an object of the class Person

Class Constructor

In the examples above, we have created an object from the Person class. However, a class without a constructor is not really useful in real applications. The properties that all Person objects must have are defined in a method called __init__(). __init__() initializes each new instance of the class. Let us use the constructor function to make our class more useful. The __init__() constructor function has self parameter which is a reference to the current instance of the class.

Examples:

class Person:
      def __init__ (self, name):
        # self allows to attach parameter to the class
          self.name =name

p = Person('Odion')
print(p.name)
print(p)

Output:

Odion
<__main__.Person object at 0x2abf46907e80>

Let us add more parameters to the constructor function.

class Person:
      def __init__(self, firstname, lastname, age, country, city):
          self.firstname = firstname
          self.lastname = lastname
          self.age = age
          self.country = country
          self.city = city


p = Person('Odion', 'Sonny', 200, 'Nigeria', 'Lagos')
print(p.firstname)
print(p.lastname)
print(p.age)
print(p.country)
print(p.city)

Output:

Odion
Sonny
200
Nigeria
Lagos

Object Methods

Objects can have methods. The methods are functions that belong to the object and are defined inside a class.

Example:

class Person:
      def __init__(self, firstname, lastname, age, country, city):
          self.firstname = firstname
          self.lastname = lastname
          self.age = age
          self.country = country
          self.city = city
      def person_info(self):
        return f'{self.firstname} {self.lastname} is {self.age} years old. He lives in {self.city}, {self.country}'

p = Person('Odion', 'Sonny', 200, 'Nigeria', 'Lagos')
print(p.person_info())

Output:

Odion Sonny is 200 years old. He lives in Lagos, Nigeria

Object Default Methods

Just like when writing functions in python, sometimes you may assign default values for your object methods. By giving default values to the parameters in the constructor, we can avoid errors when we call or instantiate our class without parameters. Let's see how it looks:

Example:

class Person:
      def __init__(self, firstname='Odion', lastname='Sonny', age=200, country='Nigeria', city='Lagos'):
          self.firstname = firstname
          self.lastname = lastname
          self.age = age
          self.country = country
          self.city = city

      def person_info(self):
        return f'{self.firstname} {self.lastname} is {self.age} years old. He lives in {self.city}, {self.country}.'

p1 = Person()
print(p1.person_info())
p2 = Person('John', 'Doe', 60, 'Ghana', 'Accra')
print(p2.person_info())

Output:

Odion Sonny is 200 years old. He lives in Lagos, Nigeria.
John Doe is 60 years old. He lives in Accra, Ghana.

Method to Modify Class Default Values

In the example below, the Person class, all the constructor parameters have default values. In addition, we have hobbies parameter, which we can access using a method. Let us create add_hobbies method to add hobbies to the hobbies list.

class Person:
      def __init__(self, firstname='Odion', lastname='Sonny', age=200, country='Nigeria', city='Lagos':
          self.firstname = firstname
          self.lastname = lastname
          self.age = age
          self.country = country
          self.city = city
          self.hobbies= []

      def person_info(self):
        return f'{self.firstname} {self.lastname} is {self.age} years old. He lives in {self.city}, {self.country}.'
      def add_hobbies(self, hobbies):
          self.hobbies.append(hobbies)

p1 = Person()
print(p1.person_info())
p1.add_hobbies('Swimming')
p1.add_hobbies('Basket ball')
p1.add_hobbies('Programming')
p2 = Person('John', 'Doe', 60, 'Ghana', 'Accra')
print(p2.person_info())
print(p1.hobbies)
print(p2.hobbies)

Output:

Odion Sonny is 200 years old. He lives in Lagos, Nigeria.
John Doe is 60 years old. He lives in Accra, Ghana.
['Swimming', 'Basket ball', 'Programming']
[]

Inheritance

Using inheritance we can reuse parent class code. Inheritance allows us to define a class that inherits all the methods and properties from the parent class. The parent class or super or base class is the class that gives all the methods and properties. The child class is the class that inherits from another or parent class. Let us create a student class by inheriting from person class.

class Student(Person):
    pass


s1 = Student('Temilade' 'Openiyi' 27, 'Nigeria', 'Lagos')
s2 = Student('Ayodeji', 'Balogun', 31, 'Nigeria', 'Lagos')
print(s1.person_info())
s1.add_hobbies('Shopping')
s1.add_hobbies('Dancing')
s1.add_hobbies('Singing')
print(s1.hobbies)

print(s2.person_info())
s2.add_hobbies('Jogging')
s2.add_hobbies('Drawing')
s2.add_hobbies('Skiing')
print(s2.hobbies)

Output:

Temilade Openiyi is 27 years old. He lives in Lagos, Nigeria.
['Shopping', 'Dancing', 'Singing']
Ayodeji Balogun is 31 years old. He lives in Lagos, Nigeria.
['Jogging', 'Drawing', 'Skiing']

We did not call the __init__() constructor in the child class. If we didn't call it then we can still access all the properties from the parent. But if we do call the constructor we can access the parent properties by calling super().
We can add a new method to the child or we can override the parent class methods by creating the same method name in the child class. When we add the __init__() function, the child class will no longer inherit the parent's __init__() function.

Overriding parent method

class Student(Person):
    def __init__ (self, firstname='Odion', lastname='Sonny' ,age=200, country='Nigeria', city='Lagos', gender='male'):
        self.gender = gender
        super().__init__(firstname, lastname, age, country, city)
    def person_info(self):
        gender = 'He' if self.gender == 'male' else 'She'
        return f'{self.firstname} {self.lastname} is {self.age} years old. {gender} lives in {self.city}, {self.country}.'

s1 = Student('Temilade' 'Openiyi' 27, 'Nigeria', 'Lagos', 'female')
s2 = Student('Ayodeji', 'Balogun', 31, 'Nigeria', 'Lagos', 'male')
print(s1.person_info())
s1.add_hobbies('Shopping')
s1.add_hobbies('Dancing')
s1.add_hobbies('Singing')
print(s1.hobbies)

print(s2.person_info())
s2.add_hobbies('Jogging')
s2.add_hobbies('Drawing')
s2.add_hobbies('Skiing')
print(s2.hobbies)
Temilade Openiyi is 27 years old. She lives in Lagos, Nigeria.
['Shopping', 'Dancing', 'Singing']
Ayodeji Balogun is 31 years old. He lives in Lagos, Nigeria.
['Jogging', 'Drawing', 'Skiing']

We can use super() built-in function or the parent name Person to automatically inherit the methods and properties from its parent. In the example above, the child method has a different feature, it can identify if the gender is male or female and assign the proper pronoun(He/She).

Encapsulation

In object-oriented programming, restricting access to methods and variables to prevent data from direct modification is known as Encapsulation. Let us look at a new class called Name.

class Name:

    def __init__(self):
        self.__defaultname = "John Doe"

    def displayName(self):
        print(f"Your name is: {self.__defaultname}")

    def resetName(self, newname):
        self.__defaultname = newname

c = Name()
c.displayName()

# change the price
c.__defaultname = "Odion Sonny"
c.displayName()

# using setter function
c.resetName("Odion Sonny")
c.displayName()

Output

Your name is: John Doe
Your name is: John Doe
Your name is: Odion Sonny

In the above program, we defined a Name class.

We used __init__() method to store the default name of Name. Here, notice the code.

c.__defaultname = Odion Sonny

Here, we have tried to modify the value of __defaultname outside of the class. However, since __defaultname is a private variable, this modification is not seen on the output.

As shown, to change the value, we have to use a setter function i.e resetName() which takes price as a parameter.

Polymorphism

Polymorphism is simply the ability of an object to have many forms. it allows us to perform the same action in many different ways. For example, the Python built-in function len() can be used to return the length of the items in a list or the length of the characters of a string. Example

list1 = ['Hello', 'World']
string1= 'Hello world'

# calculate count
print(len(list1))
print(len(string1))

Output

2
11

Polymorphism with Inheritance

In Python, Polymorphism lets us define methods in the child class that have the same name as the methods in the parent class. In inheritance, the child class inherits the methods from the parent class. However, it is possible to modify a method in a child class that it has inherited from the parent class. This is particularly useful in cases where the method inherited from the parent class doesn’t quite fit the child class. In such cases, we re-implement the method in the child class. This process of re-implementing a method in the child class is known as Method Overriding.

Example

class Artiste:
def intro(self):
    print("There are many music genres.")

def genre(self):
    print("Different artistes have their main genre.")

class Beyonce(Artiste):
def genre(self):
    print("Beyonce's main music genre is R&B/Soul.")

class Tems(Artiste):
def genre(self):
    print("Tems' main music genre is Afrobeats.")

obj_art = Artiste()
obj_bey = Beyonce()
obj_tem = Tems()

obj_art.intro()
obj_art.genre()

obj_bey.intro()
obj_bey.genre()

obj_tem.intro()
obj_tem.genre()

Output

There are many music genres.
Different artistes have their main genre
Beyonce's main music genre is R&B/Soul.
Sparrows can fly.
Tems' main music genre is Afrobeats.
Ostriches cannot fly.

And that brings us to the end of this article!

that-was-a-lot-a-lot.gif

Key Points to Remember:

  • Object-Oriented Programming makes the program easy to understand as well as efficient.
  • Since the class is sharable, the code can be reused.
  • Data is safe and secure with data abstraction.
  • Polymorphism allows the same interface for different objects, so programmers can write efficient code.

NB: Please write comments if you find anything incorrect, or if you want to share more information about the topic discussed above.

References

Â