Python Oops - Classes & Objects

In this tutorial, you will master the fundamental concept of object oriented programming(OOPs). In addition, you will learn about objects, to define a class, what an instance is, etc with examples.

Introduction to oops in python

Python being a multi-paradigm programming language enables the developer to choose any approach he wishes for scripting including procedural, functional, imperative, and object-oriented styles.Object-oriented programming, often abbreviated to OOPS,  is accepted universally because of its easiness and structural distinctiveness. As its name suggests, Objects are the pivot of the OOPS concept along with class. In other words, the foundations of object-oriented programming language are objects and classes.

Object-oriented programming in python

Object-oriented programming is a programming paradigm that enables a way of structuring programs by using the idea of “objects”. Two important characteristics of objects are attributes and behaviors.

For example, a student can be an object with attributes like name, class, id_no, etc, and the behaviors such as talking, singing, dancing, etc. Similarly, a mobile phone can be represented as an object with attributes like name, model, color, etc, and behaviors like calling, messaging, uploading, downloading, etc.

In programming, an object can be used to represent data and methods associated with the code. By bundling attributes and behaviors into individual objects, the OOPS approach is facilitating neat and well-organized codes. In addition, it also reduces redundancy by enabling code reusability. The concept of focusing on code reusability is termed as “DRY” which means Don’t Repeat Code.

Before jumping into the details let see how Object-Oriented Programming differs from Procedural Oriented Programming.

Object-Oriented Programming Procedural Oriented Programming.
Deals with data Deals with algorithm
Programs are divided into objects Programs are divided into Functions
Follows bottom-up approach Follows top-down approach
Objects can move freely within member functions Data can move between functions within the program
Contains access specifiers like private, public, and protected Contains no access specifiers
Supports inheritance Does not support inheritance
Supports overloading Does not supports overloading
Data hiding is possible Data hiding is impossible
Requires more memory Requires less memory
Highly Secure Less secure
Ex: C++,Java,Python etc Ex: C,FORTRAN,Pascal etc

What is a class

In object-oriented programming, a class is a user-defined data structure used to create objects. Users can define a set of attributes and functions, referred to as methods, associated with the object in a class. The methods in a class identify the behavior and actions that an object created in the class needs to perform on its data. In short, we can say that a class is a collection of objects or a blueprint of objects defining attributes and methods in common.

How to define a class?

So now you are well clear about what a class is. Now we will see how to define a class.
A class definition always begins with the keyword class followed by the class_name and a colon. The body of the class is scripted below following the indentation rule. 

The syntax is as follows:

class Class_name:
 . . . . . . . . 
 class_body 
 #Here you can define all attributes and methods
 . . . . . . . .  

This tutorial will teach you to create a class named Student to store some of the basic features of a student like a name, age, id no and the qualities a student can have. Here is an example:

class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age 

Now, let's scrutinize the example to get more clarity on the concept of class. In this example,

  • We have created a class with class_name  Student.
  • A class can contain two types of attributes, namely, instance attribute and class attribute.
    •   Instance attributes are attributes that show the properties( name, age, and roll no) of student objects. Instance attributes are always defined inside an initializer method named.__init__(). This method initializes the instance attributes as soon as the object is created. You can pass any number of parameters to .__init__method however the point to be considered is the first parameter should always be a variable called self.
      • self parameter is used to initialize the attributes with the parameter.
      • self.name = name indicates the creation of an instance attribute named name to which the parameter value is assigned.
      • self.age = age denotes the creation of an instance attribute named
    • Class attributes are attributes that hold the same value for all instances in the class which is always declared outside of.__init__ () method. In our case, all students belong to the computer science department. Hence Dept is declared as a class attribute just before.__init__() method.

In essence, a class attribute is used to define properties that hold the same value for all class instances whereas an instance attribute is used to define attributes that hold unique values for each class instance.

While writing a class in python, one must be careful about the indentation as indentation plays a vital role in structuring a program. You can find in the above example how structured each and every component is and how indentation is maintained :

  • The class attributes defined just beneath the class are intended by four spaces.
  • The .__init__() method is intended by four spaces while the method body uses  8 space indentation.

In case if you fail to use the correct indentation you will receive an Indentation Error.
So we have defined the Student class now. Let's create some students which are the objects of the class.

What is an object

An object, also known as an instance, is an instantiation of a class. To be more specific an object implies the instance of a class.

By defining a class we have only created the description for the object. No memory or space is allocated yet. To make it mean you need to instantiate an object. This can be accomplished by using the class_name followed by parentheses containing the values to be passed to.__init__() method.

Syntax is :

obj = Class_name() #S1 = Student() 

Where obj is an object of the class.

Now consider the scenario where you forgot to provide the parameters to be passed to.__init__() method while instantiating the object. Python will simply raise an error called TypeError as illustrated below.

class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age

S1 = Student()
S2 = Student()

Output Error:

Traceback (most recent call last):
  File "oops_ex.py", line 11, in 
    S1=Student()
TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'

To avoid this error you need to pass the parameters inside the parentheses while instantiating the object. The following example shows this:

class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age

S1 = Student("Chris", 17)
S2 = Student("Debora",16) 

In our example S1 and S2 are the two instances of the class.S1 is an object for the class containing two attributes Chris and 17. Similarly, S2 denotes the object of a class that contains two attributes Debora and 16.

How to access attributes using objects

Accessing attributes from a class is not at all a big task. Accessing can be done by using a dot operator along with the object. The following example shows the way of accessing both the instance attribute and class attribute.

Class attribute Dept is accessed using the object S1 with the help of a dot operator which outputs the result as Computer Science.

>>> print(S1.Dept)
Computer Science 

Similarly, instance attributes can be accessed using the dot operator with the object. Refer to below for example to understand the concept.

class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age

S1 = Student("Chris", 17)
S2 = Student("Debora",16)

print('Hi I am',S1.name,'and I am',S1.age,'years old')
print('Hi I am',S2.name,'and I am',S2.age,'years old')

Output:

Hi I am Chris and I am 17 years old
Hi I am Debora and I am 16 years old

Here, S1.name and S1.age indicate the accessing of instance attributes name and age from the class for the instance S1 with the help of dot operator, which produces the result Chris and 17.Similarly S2.name and S2.age access the attributes for instance S2.

How to modify attributes dynamically

In object-oriented programming, we can modify the attributes dynamically by adding new attributes, altering the attribute values, or by deleting the attributes themselves.

Adding new attributes

We have slightly modified our example by adding a new attribute S1.idno = 10001. When you observe the example you can notice that the new attribute is not defined inside the class instead it is defined outside dynamically.

class Student:
    
    Dept = 'Computer Science'
    
    def __init__(self,name,age):
        self.name=name
        self.age=age

S1 = Student("Chris", 17)
S1.idno = 10001

print('Hi Iam',S1.name,'and Iam',S1.age,'years old holding idno:',S1.idno) 

Output:

Hi Iam Chris and Iam 17 years old holding idno: 10001

Altering attribute values

It is also possible to alter the value of an attribute dynamically. See below the program where we have changed the .name attribute of S1 to Tom which was previously assigned to Chris. In a similar way modified the class attribute. Dept to Science from Computer Science.

class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age

S1 = Student("Chris", 17)
S1.name ='Tom'
S1.Dept ='Science'

print('Hi I am',S1.name,'and I am',S1.age,'years old')

Output:

Hi Iam Tom and Iam 17 years old.
Science

The salient point is that by default all custom objects are changeable. Specifically, we can say that mutable objects can be dynamically altered while immutable objects can’t be. So lists and dictionaries are changeable while strings and tuples are immutable.

Deleting attributes

You can also delete an attribute using the del keyword followed by a dot operator.

S1.idno = 10001
del S1.idno

Here it deletes the attribute id.no.

Built-in class attributes

The built-in attributes found in python are listed below. They can be accessed using the dot notation which is illustrated in the following example.

  • __dict__ :dictionary of namespaces
  • __doc__ : gives the sting documentation
  • __name__: gives the class name
  • __module__:gives the module name in which class is defined
  • __bases__:gives the base class tuple
class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age

print("Student.__dict__:",Student.__dict__)
print("Student.__doc__:",Student.__doc__)
print("Student.__name__:",Student.__name__)
print("Student.__module__:",Student.__module__)
print("Student.__bases__:",Student.__bases__) 

Output:

Student.__dict__: {'__module__': '__main__', 'Dept': 'Computer Science', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}
Student.__doc__: None
Student.__name__: Student
Student.__module__: __main__
Student.__bases__: (,)

Oops - Instance methods

Object-oriented programming supports the use of methods in the class. An instance method can be referred to as a function that is defined inside the class to specify the behaviors of the object. The key point to keep in mind is that always ensure to call the instance method from the instance of that particular class itself. Moreover, the first parameter of an instance method is always the self as in the .__init__() method.

Let’s see how to create an instance method by carefully observing the following example. In our Student class, we have two instance methods namely

  • StudDetails(): which gives the details of a student
  • StudAct(): which gives the skill of a student
class Student:
    
    #class attribute
    Dept = 'Computer Science'
    
     #instance attribute
    def __init__(self,name,age):
        self.name=name
        self.age=age

  #instance Methods
    def StudDetails(self):
        print('Hi Iam',self.name,'and Iam',self.age,'years old. ') 
 
    def StudAct(self, activity):
        print(self.name,'is good at',activity)


S1 = Student("Chris", 17)
S2 = Student('Debora',16)

S1.StudDetails()  #calling instance method StudDetails()
S2.StudDetails()

S1.StudAct("Football") #calling instance method StudAct()
S2.StudAct('Tennis') 

Here in this example, StudDetails() method is defined to print the name and age of Students. StudAct() method prints the activity in which the particular student is good at. For instance on calling, S1.StudDetails() method the control shifts to the function StudDetails defined inside the class Student where it performs the print function. The parameters defined in StudDetails is self which gives the values defined in .__init__() method. Hence it is possible for us to access the instance attributes .name and .age. And finally, the output is printed as Hi Iam Chris and Iam 17 years old.

In the same manner S1.StudAct("Football") prints the output as Chris is good at Football, where the parameter"Football" is passed to ‘activity’ argument in the method definition.

Output:

Hi Iam Chris and Iam 17 years old.
Hi Iam Debora and Iam 16 years old.
Chris is good at Football
Debora is good at Tennis