OOPs in Python

Master Python Class Methods and Attributes | Magic Methods | 8 mins read

Today we are going to talk about 'Members of a python class' .This tutorial covers types of methods and attributes in a python class. Some pythonic terminologies like Dunder methods.This tutorial describes the structure of a python class. Let's get started.

Table of Contents

Introduction

Welcome back to another exciting tutorial of Object Oriented Programming in Python series i.e “Members of a python class ”. In this Python tutorial, you’ll learn about the types of variables and methods of  a python class.

You will learn :-

  • Members of python class
  • Variables and their types
  • When and where to use particular type of variable
  • Method and their types
  • When and where to use particular type of method.

If you aren’t following along, I would recommend you to go back where you left or start from here.

Class Members in Python

Typically, a python class contains a doc string, built-in attributes, variables (attributes) and methods.They serve different aim in the class. Let us check one by one the meaning and usage of each of them.

Members of a class
Members of a class

Docstring in a Python Class

A doc string is a string literal that gives a short introduction of a class i.e. what are the attributes and methods present in it.

The constructor ,methods and subclass can have their own docstring.

It is declared between (not a hard and fast rule) triple quotes (”’ or “””) as a string. The doc string can be extended to multiple lines easily if declared between triple quotes.

Docstring of a class is accessed using __doc__ builtin attribute .

Take the example of a Student class.

>>> class Student: 
...     '''This class represent a Student Template''' 
... 
...     def __init__(self,Name,ID): 
...         '''Create and initialize Name and Id instance variables''' 
...         self.Name = Name 
...         self.Id = ID 
...       
>>> print(Student.__doc__) 
This class represent a Student Template 
>>>

Built-in Attributes of a python class

Every class even it is an empty class contains some built-in attributes. They are accessed using the dot operator.

    • __doc__ – Returns Doc string of the class or None if not defined.
    • __name__ – Returns the name of the class
    • __module__ – Returns the name of the module in which class is declared.
    • __bases__ Returns the tuple containing the names of the class that it inherited.
    • __dict__ Returns the dictionary of the namespace of the class.

Let us take an example of a class and print the result of each attribute.

>>> class A:                                                                                                            
...     pass                                                                                                            
...                                                                                                                     
>>> class B(A):                                                                                                         
...     ''' I am class B '''                                                                                            
...                                                                                                                     
...                                                                                                                     
>>> print(B.__doc__)                                                                                                     
I am class B
>>> print(B.__name__)                                                                                                   
B    
>>> print(B.__module__)                                                                                                 
__main__ 
>>> print(B.__bases__)                                                                                                  
(<class '__main__.A'>,)  
>>> print(B.__dict__)                                                                                                   
{'__module__': '__main__', '__doc__': ' I am class B '}                                                                 

Types of variables in a Python Class

We can create as many variables as we want in a class. There are different types of variables to fulfill different aspects of a class. Such as

Type of variables in a python class
Type of variables in a python class

Instance Variable

Instance variables are those which are different for every instance of the class (i.e. every instance) contains their own copies of instance variables.

One cannot access or alter the value of the instance variable of one instance using another instance.

When to use Instance variable ?

When you cannot assign a general value for a variable to all the objects of the class, you opt for instance variable.

For example :- Consider a Student class and you want to store two properties like Name and ID of student in the class.

As the Name and Id for all the objects of class Student will be different . So , instance variables are used to store them .
>>> class Student:
...     def __init__(self,ID,Name):
...             self.ID = ID
...             self.Name = Name
... 
>>> Student1 = Student('001','David')
>>> Student2 = Student('002','Harry')
>>> print(Student1.Name)
David
>>> print(Student2.Name)
Harry
>>>

In the code snippet you can see , the value of instance variable Name is different for both the student object.

All the instance variables are declared under __init__() method i.e constructor within a class.

How to access or alter Instance variable ?
  • Using self within a class
    Sometimes, we have to access instance variables within a class. Consider the example of Student class
>>> class Student:
...     def __init__(self,ID,Name):
...             self.ID = ID
...             self.Name = Name
...     
...     # This method returns the string in the prescribed form .
...     # We can see that ID and Name variables are accessed using self.
...     
...     def __str__(self):
...             return f"{self.Name} is a Student with ID {self.ID}"
...
>>> Student1 = Student('001','Harry')
>>> print(Student1)
Harry is a Student with ID 001
>>> 

  • Using Object reference
    This is the most general way of accessing instance variables of an object .
    Consider the example of Student class.
>>> class Student:
...     def __init__(self,ID,Name):
...             self.ID = ID
...             self.Name = Name
... 
>>> Student1 = Student('001','David')
>>> print(Student1.Name)
David
>>> 

The Name variable is accessed using object Student1.


  • Using Functions

There are several builtin functions to access or alter the attributes.

    • hasattr(object,Attribute_name) – It checks if the attribute present in the object or not.
    • getattr(object,Attribute_name) – It is used to access the attribute
    • setattr(object,Attribute_name,value) – To change the value of that attribute if not present , it creates a new attribute with that value.
    • delattr(object,Attribute_name) – To delete that attribute

>>> class Student:                                                                                                      
...     def __init__(self,Name,Id):                                                                                     
...             self.Name= Name                                                                                         
...             self.Id  = Id                                                                                           
...                                                                                                                     
>>>                                                                                                                     
>>> Student1 = Student('Harry','001')
>>>
>>>
>>> #Check if Name attribute is present, the result will be True 
>>> print(hasattr(Student1,'Name')) 
True
>>>
>>>
>>> # Check if Age attribute is present, the result will be false 
>>> print(hasattr(Student1,'Age')) 
False
>>>
>>>
>>> # Get the value of Name attribute 
>>> print(getattr(Student1,'Name')) 
Harry
>>>
>>>
>>> #Set the value of Name attribute to David 
>>> setattr(Student1,'Name','David')
>>>
>>>
>>> # Check if Name changes 
>>> print(getattr(Student1,'Name')) 
David
>>>
>>>
>>> # Delete the name attribute 
>>> delattr(Student1,'Name')
>>>
>>>
>>> # Check if attribute Name still exists or not 
>>> print(hasattr(Student1,'Name')) False 
>>>

Class Variable

Class variable is that variable in the class which is shared by all the instances i.e. any instance of the class can access that variable.

Class variable is defined outside the constructor.

How to access Class variable ?

When you want to declare a variable that has to be same for all the objects, class variable is used.

We can access class variable using dot operator with class name or instance .

There are few complications if you access class variable using instance . I mention those in the Python Trick section in the end. Be sure you read them carefully.

For example :- Consider a Student class and you want to store the number of students being created i.e. you want to store the number of objects being created of the Student Class.

>>> class Student:                                                                                                      
...     numOfStudent = 0                                                                                                
...                                                                                                                     
...     def __init__(self,Name,ID):                                                                                     
...             self.Name = Name                                                                                        
...             self.ID = ID                                                                                            
...             Student.numOfStudent += 1                                                                               
...                                                                                                                     
>>> # Initially number of students should be 0 (Accessing using class name)                                                                        
>>> print(Student.numOfStudent)                                                                                         
0                                                                                                                       
>>>                                                                                                                     
>>>                                                                                                                     
>>> # Adding new Student Objects                                                                                        
>>> S1 = Student('David','001')                                                                                         
>>> S2 = Student('Harry','002')                                                                                         
>>> S3 = Student('Murphy','010')                                                                                        
>>>                                                                                                                     
>>>                                                                                                                     
>>> # Now checking if the number of students be equal to 3(Accessing using instance)                                                            
>>> print(Student.numOfStudent)                                                                                         
3                                                                                                                       
>>>                                                                                                                     
>>> # Check if every intance shares the same value                                                                      
>>> print(S1.numOfStudent)                                                                                              
3                                                                                                                       
>>>                                                                                                                     
>>>                                                                                                                     
>>> print(S2.numOfStudent)                                                                                              
3                                                                                                                       
>>>  

In the code snippet you can see , we have accessed the value of variable NumOfStudent with class name and instances and the result is same for all cases.

Private Variable

There is no such thing that is private in python. Still , there is a coding convention which programmer used to establish some rules.

Name Mangling – Any identifier such as __temp_ (at most 1 trailing underscore and at least two leading underscores) is replaced by _classname__temp_. This is an implicit conversion to avoid name clashes with the subclass.

The private variables (__temp_) cannot be accessed directly by any instance outside the class. Still, you can access them using mangled name i.e _classname__temp_.

EXAMPLE – Consider a Student class with a private variable __grade_ .

>>> class Student:
...     __grade_ = "A+"
...

  • The mangled name of _Student__grade_ can be seen with the __dict__ attribute of the class.
>>> Student.__dict__
mappingproxy({'__module__': '__main__', '_Student__grade_': 'A+', '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None})

  • An error occur when we try to access the private variable outside the class.
>>> Student1 = Student()
>>> Student1.__grade_
Traceback (most recent call last):
File "", line 1, in 
AttributeError: 'Student' object has no attribute '__grade_'

  • No error occur if we use mangled name to access the variable.
>>> Student1 = Student()
>>> Student1._Student__grade_
'A+'

Note :- Private methods also follows same convention.

Types of Method in a Python Class

Type of methods in a class
Type of methods in a class

Instance Method

The methods whose first parameter is the instance (popularly termed as self) comes under the category of instance methods.

For Example:-

Consider a Student class which stores name and roll no. of a student and a instance method printName to print the name of a specific instance.

>>> class Student:
...     def __init__(self,name,roll):
...             self.name = name
...             self.roll = roll
...
...     def printName(self):
...             print("The name of the student is",self.name)
...

How to call a Instance Method ?
>>> S1 = Student("Harry",'1')
>>>
>>> # Using Instance with dot operator
>>> S1.printName()
The name of the student is Harry
>>>
>>> # Using Class and pass Instance as parameter
>>> Student.printName(S1)
The name of the student is Harry
>>>

Class Method

The methods whose first parameter is the class (popularly termed as cls) comes under the category of class methods.

For Example:-

Consider a Student class which stores name,roll no and total subjects(class variable) of a student and a class method changeTotalSubject to change number of total subjects.

>>> class Student:
...     total_sub = 5
...     def __init__(self,name,roll):
...             self.name = name
...             self.roll = roll
...
...     @classmethod
...     def changeTotalSubjects(cls,val):
...             cls.total_sub = val
...
>>>

@classmethod is a decorator.


How to call a Class Method ?
>>> # Before assigning new value
>>> Student.total_sub
5
>>>
>>> # After applying classmethod
>>> Student.changeTotalSubjects(10)
>>> Student.total_sub
10
>>>

Static Method

These methods neither takes instance object as argument nor class .They works as utility functions . As class methods , static methods are also bound to the class.

For Example:-

Consider a Student class which stores name and roll no .We have to store the roll number such as it contains exactly 5 digits. If roll number is less than 5 digits .Add leading zeroes.

So it is the programmer duty to follow this constraint using a static method.

>>> class Student:
...     def __init__(self,name,roll):
...             self.name = name
...             self.roll  = Student.rollCheck(roll)
...
...     @staticmethod
...     def rollCheck(roll):
...             return roll.rjust(5,'0')
...
>>> # Creating an instance with roll number less than 5 digit
>>> S1 = Student("Harry",'1')
>>>
>>> # roll number will be filled with leading zeroes as we call rollCheck method in the constructor
>>> S1.roll
'00001'
>>>

@staticmethod is a decorator.

Magic Method

Magic methods or Dunder (double underscore) Methods are the methods which contains two trailing and two leading underscores in their name .

The most common example of dunder method is __init__() method .Magic methods are commonly used for operator overloading.

Several other magic methods are __len__ , __str__ , __repr__ etc. We will talk about them in the Operator Overloading section.

Python Trick

Why it is not a good idea to alter class variable using an instance ?

Consider a Student class which stores name,roll no and total subjects(class variable) of a student .
>>> class Student:
...     total_sub = 5
...     def __init__(self,name,roll):
...             self.name = name
...             self.roll = roll
...
>>>

Create two instances S1 and S2

>>> S1 = Student("David",'1')
>>> S2 = Student("Harry",'2')
>>>

Update value of total_sub(class variable) using S1.

>>> # Lets change the total_sub value to 10 using instance S1
>>> S1.total_sub = 10
>>>

Check for both instance if they see the new value

>>> # Ideally we should think that the class variable value is changed and new value is shown to every object
>>>
>>> # Check if S2 see the new total subjects
>>> S2.total_sub
5
>>> # It seems S2 is still seeing the old value of total_sub
>>>
>>> # Check for S1
>>> S1.total_sub
10
>>> # S1 is seeing the updated value
>>>

Why it happens ?

  • When we update the class variable using S1 , it creates a new instance variable total_sub with the given value .
  • And when we check value of total_sub for S1, it find the variable in its instance scope and returns the value
  • It can be seen more clearly using __dict__ builtin attribute for both the instance
>>> S1.__dict__
{'name': 'David', 'roll': '1', 'total_sub': 10}
>>> S2.__dict__
{'name': 'Harry', 'roll': '2'}
>>>

Conclusion

Now , you are familiar with attributes , methods , their types and use cases .We can proceed further to understand major concepts like Inheritance,Operator Overloading etc.

Last but not the least, do leave your feedback and suggestions. We would love to hear your feedback.

Till then , keep coding.