Introduction to OOPS in Python

Just like other programming languages, we use object-oriented programming in Python to write modular, reusable, and extensible code. In other words, OOP allows us to create organized code that is easy to maintain and modify when requirements are constantly changing. In this blog, we will discuss the basic oops concept in Python: Classes and Objects, Types of methods and Types of attributes.

What is Class and Object in Python?

A class is a blueprint for an object that defines its attributes and methods. For a given class, we can create multiple objects. To create a class in Python, we use the keyword "class" followed by the name of the class and a colon. On another side, an object is an instance of a class in Python. An object is a container that holds data (attributes) and functions (methods) that operate on that data.

For example, we can create objects of an "Animal" class such as a "Dog," "Deer," and "Lion," which are based on the attributes and methods defined in the "Animal" class. Here, attributes may include characteristics such as sound, height, and type of animal (herbivore, carnivore, omnivore, etc.), and methods may include behaviours like running, eating, etc.

An important idea to explore!

In Python, everything is an object, including numbers, strings, lists, etc. This means that every value in Python is an instance of a class, and it has attributes and methods associated with it.

For example, if we define a string in Python, like "Hello World," this string is an object of the str class in Python. It has properties like its length, its characters, and its methods like upper() and lower() which can be used to manipulate the string. Similarly, a list in Python is an object of the list class. It has properties like its length and the elements it contains, and methods like append() and pop() which can be used to add or remove elements from the list.

Let's understand class and object using code examples

class employee_details:
    pass

# creating object of employee_details class   
emp1 = employee_details()
emp1.name = "Mohan Kumar"
emp1.experience = "3.5 years"
print(emp1.name)
print(emp1.experience)

# output
Mohan Kumar
3.5 years

In the example above, we created an empty class named employee_details using the pass statement. As a result, we have not defined any attributes or methods for the class.

Pass statement: It is possible to create an empty class in Python by using a statement called "pass". The pass statement serves as a placeholder, which allows the code to move on to the next line without executing any instructions. In other words, the pass statement can be useful when writing code that is not yet fully formed. It is often used with control flow statements, empty functions, or empty classes.

In the above code, we are setting the values manually for the object emp1 of the "employee_details" class. If we do not want to set the values manually, we can use the "_init_" function to initialize the object's attributes.

class employee_details:
    def __init__(self, name, experience):
        self.name  = name
        self.experience = experience

emp1 = employee_details("Mohan Kumar", "3.5 years")
print(emp1.name)
print(emp1.experience)
          
# output
Mohan Kumar
3.5 years

In the above example, the "_init_" method takes three parameters: self, name, and experience. When we create a new object of the "employeedetails" class and provide values for name and experience, these values are automatically assigned to the object through the "\init_" method. This simplifies the process of creating new objects and setting their attributes without having to manually set the values each time.

_init_ method: Each class should have a method called _init_ which is the first method called when an object of the class is created. This method is used to assign values to object properties and perform other necessary operations, such as initialization (constructor method).

self parameter: self refers to the current instance of the class. Although a class is defined to describe attributes and behaviours of an object, each instance of the class will have its own unique set of attributes. By using self, we can access and modify the attributes (variables) and methods of the class, as well as call other methods of the same instance.

Now, to make a class more effective, it should have methods that perform specific tasks. For example, in the following code, we have created a method called "employee_experience()" that can access "name" and "experience" attributes.

class employee_details:
    def __init__(self, name, experience):
        self.name  = name
        self.experience = experience
        
    def employee_experience(self):
        return self.name + "has experience of -" + str(self.experience)
        
emp1 = employee_details("Mohan Kumar", "3.5 years")
print(emp1.employee_experience())

# output
Mohan Kumar has experience of - 3.5 years

Some important points to note

  • In object-oriented programming with Python, "self" refers to the current instance of a class. To properly use "self", it must be included as the first parameter in both the instance method and constructor. If "self" is not provided, an error will occur.
  • Although "self" is a convention and not a reserved Python keyword, it is recommended to use it as a parameter name for better readability and adherence to good programming practices. However, the user can choose another parameter name if they prefer.

Here is another code example

In this example, we have defined a Calculator class with four arithmetic methods and an operate method. The operate method calls all four arithmetic methods of the same instance using the self keyword.

class Calculator:
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        
    def add(self):
        return self.num1 + self.num2
    
    def subtract(self):
        return self.num1 - self.num2
    
    def multiply(self):
        return self.num1 * self.num2
    
    def divide(self):
        return self.num1 / self.num2
    
    def operate(self):
        result_add = self.add()
        result_subtract = self.subtract()
        result_multiply = self.multiply()
        result_divide = self.divide()
        return f"Addition: {result_add}, Subtraction: {result_subtract}, Multiplication: {result_multiply}, Division: {result_divide}"

# creating object of Calculator class
calc1 = Calculator(10, 5)

# calling the operate method of the calc1 instance
print(calc1.operate())

# output
Addition: 15, Subtraction: 5, Multiplication: 50, Division: 2.0

Types of Methods in Python

Instance methods

"Instance methods" in Python are bound to a specific object instance and are the most commonly used method for accessing and manipulating the data associated with a particular object instance. Any method created inside a class is an instance method unless otherwise specified.

class Demo_Class():

  def demo_instance_method(self):
     return "This is instance method!"

obj = Demo_Class()
obj.demo_instance_method()

As discussed above, Instance methods have one default parameter, called "self," which points to an instance of the class. While you can change the name of this parameter, it would be best to stick with the convention of using "self."

Class methods

Class methods are used to set or retrieve information about the class itself rather than a specific object instance. So they are bound to the class rather than an object instance and cannot access or modify specific instance data. To define a class method, we must use the @classmethod decorator.

class Demo_Class():

  @classmethod
  def demo_class_method(cls):
    return "This is a class method."
    
# Accessing class methods with the help of a class instance.   
obj = Demo_Class()
obj.demo_class_method()
# Accessing the class methods without creating a class instance. 
# Or accessing the class method using the class name 
Demo_Class.demo_class_method()
  1. Class methods also have a default parameter called "cls," which refers to the class itself. Using "cls" as the parameter name is common, but it is not mandatory.
  2. Class methods are useful for operations that involve the class as a whole rather than specific class instances. Note: We can not call instance methods using the class name.

Types of attributes in Python

Class or Static Variable

In Python, a class or static variable is a variable that is shared by all instances of a class. It is defined inside the class but outside of any class methods. This means that it can be accessed using the class name or any instance of the class. Let's consider the following example code:

class Car:
    # Class variable
    wheels = 4

    def __init__(self, make, model):
        # Instance variables
        self.make = make
        self.model = model

    # Static method
    @staticmethod
    def honk():
        print("Honk honk!")

# Accessing class variable
print(Car.wheels)  # Output: 4

# Creating instances of Car class
car1 = Car("Honda", "Civic")
car2 = Car("Toyota", "Corolla")

# Accessing instance variables
print(car1.make)  # Output: Honda
print(car2.model)  # Output: Corolla

# Modifying class variable
Car.wheels = 3

# Accessing class variable through instance
print(car1.wheels)  # Output: 3
print(car2.wheels)  # Output: 3

# Modifying class variable through instance
car1.wheels = 5

# Accessing class variable
print(Car.wheels)  # Output: 3

# Accessing class variable through instance
print(car1.wheels)  # Output: 5
print(car2.wheels)  # Output: 3

In the above code, the Car class has a class variable wheels that is set to 4 by default. So, we can access the class variable wheels using the class name Car and modify it by assigning a new value to it.

We can also access the class variable through instances of the Car class like car1 and car2, but modifying it through instances will create a new instance variable with the same name that only exists within that instance.

Instance variable

In Python, an instance variable is a variable that is specific to each instance of a class. It is defined inside a class method, usually, the _init_ method, and can be accessed using the instance name. Let's consider the following example code:

class Car:
    def __init__(self, make, model):
        # Instance variables
        self.make = make
        self.model = model
        self.odometer = 0

    def drive(self, miles):
        self.odometer = self.odometer + miles

# Creating instances of Car class
car1 = Car("Honda", "Civic")
car2 = Car("Toyota", "Corolla")

# Accessing instance variables
print(car1.make)  # Output: Honda
print(car2.model)  # Output: Corolla

# Modifying instance variables
car1.drive(50)
car2.drive(100)

# Accessing modified instance variables
print(car1.odometer)  # Output: 50
print(car2.odometer)  # Output: 100

Overall, instance variables allow us to define attributes that are unique to each instance of a class, and modify them as needed.

Some key points to know about Python variables and methods

In Python, dir function helps us inspect an object and obtain a list of all the attributes and methods that can be called on it. This can be useful when working with a new object in Python and you want to explore what you can do with it. On another side, dir function, we can help us save time and reduce the likelihood of making errors by ensuring that we use the correct attribute or method for a given object.

For example, if we call dir function with car1 attributes in the above example, we will get list of attributes and methods that can be called on car1 object.

print(dir(car1))

# output
['__class__', 
 '__delattr__', 
 '__dict__', 
 '__dir__', 
 '__doc__', 
 '__eq__', 
 '__format__', 
 '__ge__', 
 '__getattribute__', 
 '__gt__', 
 '__hash__', 
 '__init__', 
 '__init_subclass__', 
 '__le__', 
 '__lt__', 
 '__module__', 
 '__ne__', 
 '__new__', 
 '__reduce__', 
 '__reduce_ex__', 
 '__repr__', 
 '__setattr__', 
 '__sizeof__', 
 '__str__', 
 '__subclasshook__', 
 '__weakref__', 
 'drive', 
 'make', 
 'model', 
 'odometer']

Above all, those with an underscore are default methods and attributes in Python. Others are user-defined methods and attributes.

Built-in-attributes: Built-in attributes are predefined attributes and functions provided by Python. For instance, dict is a built-in attribute that returns all key-value pairs of an object as a dictionary. For example:

print(car1.__dict__)

# output
{'make': 'Honda', 
 'model': 'Civic', 
 'odometer': 50}

Default classes examples in Python

Python has various built-in data types:

  • List: [1, 2, 3]
  • Integer: 1, 2, 3
  • Float: 1.1, 1.4, 1.5
  • Dictionary: {1:2, 3:4}
  • String: "hello", "help", "hello world"

Each data type is considered a class in Python. When we define a class, such as the int class for the numbers 1, 2, and 3, we create three objects or instances of the int type. We can then use the methods associated with these classes, such as the sort() and reverse() methods of the list class.

These built-in methods provided by Python are beneficial for us as programmers, as they allow us to quickly and easily perform complex operations on our data structures without having to write the code ourselves.

list_example = [1,3,5,9,0]
# sorting the list
list_example.sort()
print(list_example)

# reversing the list
list_example.reverse()
print(list_example)

###output
[0, 1, 3, 5, 9]
[9, 5, 3, 1, 0]

We will cover other Python OOPS principles like encapsulation, abstraction, inheritance and polymorphism in a separate blog. Enjoy learning, Enjoy Python.

Share Your Insights

More from EnjoyAlgorithms

Self-paced Courses and Blogs

Coding Interview

Machine Learning

System Design

Our Newsletter

Subscribe to get well designed content on data structure and algorithms, machine learning, system design, object orientd programming and math.