Object-Oriented Programming(OOP) in Python

Object-oriented programming is an approach of structuring our programs in a more organized way by the use of classes and objects. Object-oriented programming(generally termed as OOP) is a widely used concept in the software industry and it can help us writing powerful applications.

When writing powerful applications such as video games, or any other application that contains a lot of data, it becomes very difficult to manage the code. In such situations, the object-oriented programming approach can help us a lot as it bundles similar properties and behaviors into individual objects and makes our code more organized and reusable.

In this tutorial, you will learn the basics of object-oriented programming in Python with the help of examples.

What is Object-Oriented Programming?

Object-oriented programming is a programming paradigm that provides us different ways to structure and organize large programs by bundling properties and behaviors with the help of individual objects.

You can think of any real-world entity as an object. For example, a person which has properties like, name, age, gender, and behaviors like, eating, sleeping, running, OR an object could represent a car that has properties like name, brand, price, and behaviors like moving and accelerating.

In the object-oriented programming approach, every real-world entity is considered as an object. Where each of these objects has some data associated with it along with some functions that can perform operations on this data.

The object-oriented approach is not the only one that is used. There is one more programming approach known as Procedural programming. But it is less common in use as it has some disadvantages.


Why Object-Oriented Programming?

Now that we have understood what object-oriented programming is. Let’s understand why should one choose this approach.

Let’s say that you got a project in which you have to keep track of university students. Where each student has some basic information like name, age, gender, and marks.

One approach to store this information is by using Python Lists. Where each list contains information of a single student. So, It can be something like this:

john = ['John Doe', 23, 'Male', '88.8%']
jennifer = ['Jennifer Smith', 22, 'Male', '92.5%']
james = ['James Bond', 24, '68.8%']

This approach has several issues:

First, it will make the code larger and unorganized which will become difficult to manage if the number of students is very high.

Second, you will have to remember that the 0 index represents the student’s name, 1 index represents student’s age, and so on. Which is difficult and if forgot you can get unexpected results.

Third, let’s say that one student does not have the whole information. Then you might face some critical error. For example, in the third list, the student ‘james’ does not have the ‘gender’ field. Therefore, if you try to fetch james[2], you will get gender as ‘68.8%’. Which is ridiculous.

All these issues can easily be solved with the object-oriented programming approach. I think now it’s clear why do we choose the object-oriented approach over other approaches.


Understanding Classes and Objects

Classes and objects are the building blocks of object-oriented programming. But what are they? What is a class?

Well, a class is just a blueprint or prototype of something that we want to create. It only defines the properties and behaviors but actually contains no data.

We can think of the term ‘Dog’ as a class. This Dog class specifies that a name, age, and color are necessary to define a dog. But, it does not contain the name, age, or color of any particular dog. It is just a blueprint which defines dogs.

While the class is a blueprint, an object or instance is something which is built from this class. The objects are real-world entity and they do contain some data in the form of properties.

As the objects are the instances of the class, they do contain actual data. Now, If we built an object from the Dog class, it means it’s real and definitely contains some data. Now it’s not just a blueprint, rather, it’s representing some real dog which has some name say, Max, which is 3 years old and black in color.

As a class is just a blueprint, therefore, we can create any number of objects from this single class.

Did you know?
In Python, everything is an object. Lists, strings, integers, numbers, sets, even classes themselves, all are objects.


Creating a class in Python

To create a class in Python we use the class keyword followed by the name of the class and a colon(:). The code that is indented inside the class definition is known as the body of the class.

Below is an example of the Dog class:

class Dog:
    pass

The Dog class defined above is an empty class. It just has a single statement the pass keyword for now. We will very soon define the actual Dog class.

The pass keyword is used as a placeholder for future code. It lets us define an empty class without throwing any error.


Creating an Object in Python

In the previous example, we created an empty Dog class. Till now, no memory is allocated as classes are just blueprint and they do not contain any actual data.

The memory is allocated as soon as we create an object. To create an object in Python, we use the class name followed by a pair of parentheses. We can create an object from the Dog class as follows:

obj = Dog()

Here, obj is an object of the class Dog.

The Dog class that we have defined till now is empty. Therefore, all the objects created from it will also be empty. Let’s now update our existing Dog class and define an actual Dog class so that all of its objects can have some real data.

To make things simple, we will just keep the name and age of the dog. To create each of the Dog class objects with these two properties(name & age), we have to use the inbuilt __init__() method.

The __init__() method is similar to a constructor in Java and C++. Each time you create an object, this method is implicitly called and sets the properties of each object. In simple words, the __init__() method initializes each of the class objects.

The __init__() method can take in any number of parameters. But, the first parameter must always be the self. The self parameter is similar to this keyword in Java and C++.

When we create a new object, this object is passed to the __init__() method as the self parameter. This self parameter is then used to set the values of the current object’s attributes.

In simple words, we can say that the self parameter refers to the current object that we are working with.

Finally, let’s update our Dog class so that it creates name and age attributes to each of the Dog class objects:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

obj = Dog('Max', 3)

The above example does the following three tasks:

  • The statement obj = Dog('Max', 3) creates a new object named obj. As soon as this statement is executed, the __init__() method is called implicitely and the dog’s name and age is passed to it as arguments.
  • The statement self.name = name creates an attribute called name and assigns to it the value of the name parameter i.e. ‘Max’.
  • The statement self.age = age also creates an attributed called age and assigns to it the value of the age parameter i.e. 3.

self should always be the first parameter of any method in the class, even if the method does not use it.


Accessing Object Attributes in Python

In our previous example, we created a Dog class object obj which contained the name and age attributes. Well, that’s fine but how can one access these attributes?

Well, accessing an object’s attributes is quite easy in Python. To access the object attributes you can use the dot(.) notation. This is done by typing the name of the object followed by a dot notation and then the name of the attribute.

For example, to access the name attribute of the object obj use the following:

obj.name # Returns name attribute's value

Let’s collect our code and try printing information of the dog ‘Max’.

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

obj = Dog('Max', 3)

print('Dog name: ', obj.name)
print('Dog age: ', obj.age)

Output:

Dog name:  Max
Dog age:  3

We can create any number of objects from the Dog class. In the below example we have created two objects from the Dog class just to make things a little clear.

Example:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

dog1 = Dog('Max', 3)
dog2 = Dog('Rocky', 4)

# Print dog1 Data
print('Dog name: ', dog1.name)
print('Dog age: ', dog1.age)

# Print dog2 Data
print('\nDog name: ', dog2.name)
print('Dog age: ', dog2.age)

Output:

Dog name:  Max
Dog age:  3

Dog name:  Rocky
Dog age:  4

Class Attributes in Python

Just like objects, a class can also have its attributes. A class attribute is something that is shared among all of the class objects. The value of the class attribute remains the same for all of its objects.

For example, all dogs are mammal species. So, one way to do this is adding a species attribute to all of the Dog class objects individually. But, that will make our code redundant. Instead, we can make it the Dog class attribute.

When we define a class attribute, it is guaranteed that all of the class objects will definitely have that attribute.

Unlike objects, a class attribute is defined outside the __init__() method and it can be accessed by all of the class objects just like a normal object attribute.

In the example below, we have introduced a class attribute named species to our existing Dog class:

class Dog:
    # class attribute
    species = 'mammal'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age

obj = Dog('Max', 3)

print('Dog name: ', obj.name)
print('Dog age: ', obj.age)
print('Dog species: ', obj.species)

Output:

Dog name:  Max
Dog age:  3
Dog species:  mammal

Class Methods

The Dog class that we are working with so far just keeps the information of each dog instance created from it. But, it doesn’t actually do anything. To add the functionality we have to define the class methods.

A class method is a function that is defined inside the body of a class and can only be called from an instance of that class.

Just like the __init__() method the first parameter of a class method must always be self. Any of the class instances can call a class method using the dot(.) notation.

Example :

class Dog:
    # class attribute
    species = 'mammal'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    # class method
    def dogInfo(self):
        print(self.name, 'is', self.age, 'years old.')

dog1 = Dog('Max', 3)
dog2 = Dog('Rocky', 4)

# call class methods
dog1.dogInfo()
dog2.dogInfo()

Output:

Max is 3 years old.
Rocky is 4 years old.

Note: We don’t have to pass the value of the self parameter when calling a method. It is implicitly passed by Python itself.
self is just a conventional name of the first parameter. you can name it whatever you want.


Passing Parameters to Methods

self is not the only parameter that a method can have. We can also pass extra parameters to a method to pass extra information. See the bark() method in the example below:

class Dog:
    # class attribute
    species = 'mammal'
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    # class method
    def dogInfo(self):
        print(self.name, 'is', self.age, 'years old.')
        
    # class method with an extra parameter
    def bark(self, sound):
        print(self.name, 'says: ', sound)

dog1 = Dog('Max', 3)
dog2 = Dog('Rocky', 4)

# call class methods
dog1.dogInfo()
dog1.bark('Woof woof')

dog2.dogInfo()
dog2.bark('bow bow')

Output:

Max is 3 years old.
Max says:  Woof woof
Rocky is 4 years old.
Rocky says:  bow bow

Note that we have to pass the value of the extra parameter of a method when we call it. As we did in the above example when calling the bark() method. If we don’t pass the value, we will get a TypeError.


Deleting Class, Objects and Attributes

Garbage collection is a very important aspect of any good programming language. Likewise, Python lets us delete a lot of stuff including classes, objects, and their attributes.

To delete a class, object, or attribute in Python, we use the del keyword.

Example : Delete object attribute

class Dog:
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
dog = Dog('Max', 3)

print('Before deleting- ')
print('Dog name: ',dog.name)

# Delete name attribute from dog
del dog.name

print('\nAfter deleting- ')
print('Dog name: ',dog.name)

Output:

Before deleting- 
Dog name:  Max

After deleting- 
Traceback (most recent call last):
  File "<string>", line 16, in <module>
AttributeError: 'Dog' object has no attribute 'name'

Just like the object attributes, we can delete objects and classes as well.

To delete the dog object use:

del dog

To delete the Dog class use:

del Dog