×

Classes and Constructors OOPS Concept – Python

OOP’s Concept is a programming concept that uses objects and classes in programming. OOP’s is all about objects and everything we create/write is an object. Objects provide a better and clear way of understanding.

Python is also an Object-Oriented Language and allows us to develop applications based on the oops concept.

OOPS vs Procedure Oriented Programming

  • In OOP’s everything is an object, thus the basic building block are objects
  • Importance given to data rather than functions
  • Follows Bottom-up approach
  • Provides data security by using access specifiers such as Public, Private, protected, etc.,
  • Easy to develop and maintain the code
  • Easy to modify since the objects are independent
  • Programming languages like C++, Python, Java, PHP, etc., use the Object Oriented approach
  • In procedural oriented a particular task is broken down into several tasks known as Functions, thus basic building blocks are Functions
  • Importance given to functions rather than data
  • Follows Top-down approach
  • Doesnot provide much security as compared to OOP’s, since the data is accessible to all the functions
  • As the code gets lengthy it becomes difficult to maintain
  • Modifications are difficult and might impact the control flow
  • Programming languages such as C, Pascal, Fortran, etc., use procedural oriented approach

Concepts of OOP’s

There are 7 basic concepts of OOP’s and they are:-

  • Class
  • Object
  • Method
  • Inheritence
  • Polymorphism
  • Data Abstraction
  • Encapsulation
Driver Classflow 3

Class

In Python, everything is an object. To create objects we require some Model or Blueprint which is nothing but Class. A class is a code template for creating objects.

Classes contain variables and methods which is the concept of Object-Oriented Programming ( OOPS ).

We can create a class to represent the properties and actions of an object. Where properties can be represented as variables and actions can be represented as methods.

Defining a Class

To define a class we can use the class keyword. Which is followed by the class name and then the body of the class within the same block.

The class name can be either capital or lower but underscore, spaces, special symbols are not allowed.

class A:
    def func_1():
        ....
    def func_2():
        ....

We can pass the details of the class such as the name of the class, what is the functionality of the class, etc., in the form of a string. This string is known as a documentation string.

We can access this documentation string using two methods, either using .__doc__ or the other one help().

class Calculator:
    ''' Calculator class for addition, division, multiplication'''
    def func_1():
        ....
    def func_2():
        ....
print(Calculator.__doc__)

# Output
Calculator class for 
addition, division, multiplication
help(Calculator)

# Output
class Calculator(builtins.object)

Calculator class for 
addition, division, multiplication

-----
----

Object of a Class

The physical existence of the class is known as an object. We can create as many objects as we require. An Object is created for every instance of the class.

Objects have states or properties (variables of the class) and behavior or actions (methods of the class) of the class. These properties and actions can be accessed by allocating a reference variable for the object.

We can create an instance using the syntax provided below.

ref_var = class_name()

The above line of code is known as the instance of the class and it creates an object. Thus an object will be created for every instance of the class. The attributes of the class are present inside the object of the class.

Slicing 3
  • An object is created when we instantiate the class. For every instance of the class an object is created
  • Object contain all the attributes of the class such as variables and methods
  • To access these attributes of the class we need to create instance which creates the object and using this object we can access these properties
  • which in turn the contents of the object can be accessed using the reference_variable

Reference Variable of a class

Reference Variable is used to refer to an object. To access the methods and variables outside of the class we need an object and where the object can be referred using reference variable.

If Else 5

In the below example, since the attributes of the class are in the object we are accessing those from outside by referring to the object of the class. Th ref_var points to the

class employee:
    def __init__(self):
        #print(self, id(self))
        self.employee_name = "sunny"
        self.employee_age = 34
    def empdetails(self):
        print(self.employee_name)
        print(self.employee_age)
    
ref_var = employee()
ref_var.empdetails()

# Output
sunny
34

Reference Variable “self”

To access the members of the class from inside of the class we need another reference variable. This reference variable points to the current object of the class. This reference variable should be declared as the first argument while defining a method inside a class.

We can name the reference variable inside the class as anything, but universally for easy understanding “self” is used as naming the reference variable.

Using this “self” we can refer to the object of the class from inside of the class.

class employee:
    def __init__(self, employee_name, employee_age):
        self.employee_name = employee_name
        self.employee_age = employee_age
    def empdetails(self):
        print(self.employee_name, self.employee_age)
    
ref_var = employee("john", 35)
ref_var.empdetails()

# Output
john 35

Basically the reference variable(“self") inside the class and reference variable(“ref_var“) outside the class points to the current object. We can conclude it by using the id() function.

If Else 6

If the memory address of both reference variables is the same then it means both of them point to the same object.

class employee:
    def __init__(self):
        print(id(self))
    def empdetails(self):
        pass
    
ref_var = employee()
print(id(ref_var))

# Output
2606851181968
2606851181968

Apart from “self”, we can name the reference variable as anything based on our requirement.

As we can from the below example, instead of self we have used inner_ref as a reference variable inside the class. Everything is executed well and the output is the same.

class employee:
    def __init__(inner_ref, employee_name, employee_age):
        inner_ref.employee_name = employee_name
        inner_ref.employee_age = employee_age
    def empdetails(inner_ref):
        print(inner_ref.employee_name, inner_ref.employee_age)
    
outer_ref = employee("john", 35)
outer_ref.empdetails()

# Output
john 35

But it is highly recommended as universally most of the programmers use “self” so that it will be easy to understand.

Constructors in Python

Constructor is a special method in python. The name of the constructor is always __init__(self). We don’t have to explicitly call a constructor, it is executed by the Python Virtual machine whenever an object is created for the class.

We can also call the constructor explicitly but it will execute the constructor for one more time since when an object is created during class instantiation, it will automatically execute the constructor.

# Constructor executed automatically
# when object is created for the class
class tutorial:
    def __init__(self):
        print("hi")
t = tutorial()

# Output
hi
# Constructor gets executed 2 times
# once by defualt, during object creation
# second during explicit call
class tutorial:
    def __init__(self):
        print("hi")
t = tutorial()
t.__init__()
# Output
hi
hi

When we define a constructor then at least one argument has to be passed into it and is known as “self”. If we don’t create a Constructor a default constructor is provided and executed by Python.

The main purpose of the constructor is to declare and initialize the instance variables of the class.

In the below example, since we are passing the instance variables country_name and country_currency, these have to be initialized by the constructor to store them in the object of the class.

When we initialize the instance variables they are stored in the object, self.country_name and self.country_currency declares the instance variable and assigning them value makes initialization.

class country:
    def __init__(self, country_name, country_currency):
        self.country_name = country_name
        self.country_currency = country_currency
        
    def details(self):
        print(self.country_name, " -> ", self.country_currency)
    
ref_var = country("US", "$")
ref_var.details()

# Output
US -> $

Thus the initialized instance variables can be accessed from the object using the reference variable.

In the below example, the initialized variable can be accessed from the object this proves that we are declaring and initializing the instance variables into the object of the class.

class country:
    def __init__(self, country_name, country_currency):
        self.country_name = country_name
        self.country_currency = country_currency
        
ref_var = country("US", "$")
print(ref_var.country_name)
print(ref_var.country_currency)

# Output
US 
$

If we declare two or more constructors then the bottom-most constructor is executed and the number of instance variables that can be passed depends on the bottom-most constructor. This is because Python does not support method overloading.

In the below example, there are three constructors with the same name, and since the python by default executes the constructor without calling it, the bottom-most one is chosen to execute.

class const:
    def __init__(self):
        print("first constructor")
    def __init__(self):
        print("second constructor")
    def __init__(self):
        print("third constructor")
c = const()

# Output
third constructor

Few points about constructors

  • Constructors are the special methods and they are executed by default when the object is created
  • They are executed once per object of the class
  • The name of the constructor has to be __init__(self)
  • We can declare and initialize the instance variables of the class using constructors
  • Constructors are optional to create, if we dont create a constructor then python provides a default constructor and executes it
  • If we create a constructor atleast one argument has to be passed such as reference_variable(self)

Constructors vs Methods

  • Constructor name has to be __init__(self)
  • Constructors are executed automatically and we dont have to explicityl call them, they are executed when we create an object for the class
  • Per object constructor will be executed only once
  • Inside constructor we have declare and initialize the instance variables of the class
  • Method name can be anything such as func(self), name(self), me(self), etc.,
  • Methods are not executed automatically, they have to be explicitly called to get them executed
  • Per object we can call methods any number of times
  • Inside method we can write the business logic

Types of Variables in a Class

There are different types of variables in a class. Based on the type of declaration and their usage in different methods, they are classified into three types:-

  • Instance Variables
  • Class or Static Variables
  • Local Variables

Instance Variables

Instance variables are those that change their values from object to object because they have different values for each object instance, such types of variables are called instance level or object level variables.

Instance variables are declared in a constructor __init__. Instance variables are initialized when an object is instantiated, and are accessible to all the constructors, methods, or blocks in the class.

# var_1 and var_2 are instance variables
class variables:
    def __init__(self, var_1, var_2):
        self.var_1 = var_1
        self.var_2 = var_2

ref_var = variables("hello", "hi")

For every object, a copy of instance variables is created with their values assigned and stored in the object.

These instance variables are first declared and then initialized inside the constructor( __init__ ). Thus when the object is created they are stored in it.

If Else 8

These instance variables can be accessed using the reference_variables. If we access them from outside of class then they need to be accessed using the object reference variable( ref_var ). If we access them from inside of the class then we need to use reference variable( self ).

class variables:
    def __init__(self, value_1, value_2):
        self.value_1 = value_1
        self.value_2 = value_2

ref_var = variables(10, 20)
print(ref_var.value_1)
print(ref_var.value_2)

# Output
10
20

We can declare the instance variables at various places such as the __init__(self) constructor, or any instance method, or even outside of the class.

  • Inside of the constructor __init__(self) we can declare and initialize an instance variable. To declare it we need to use reference variable(self)
  • Inside any instance method also we can declare an instance variable but it has to be declared using reference variable(self). An instance method is the one which has the first argument as “self”
  • Outside of the class also we can declare an instance variable. To declare it we need to use outer reference variable which is used to refer to the object of the class
class variables:
    def __init__(self, value_1):
        # Instance variable declared inside __init__
        self.value_1 = value_1
    # Instance method
    def func(self):
        # Instance variable declared inside instance method
        self.value_2 = 20
ref_var = variables(10)
# Instance variable declared outside of class using ref_var
ref_var.value_3 = 30

We can retrieve all the instance variables that are in the current object using __dict__. It returns the variables and their values as key-value pairs.

Note:- if we declare an instance variable in a method or outside of class then they need to be executed first to reflect them in the object of the class.

# instance variables inside the object after
# calling the method
class variables:
    def __init__(self, value_1):
        self.value_1 = value_1
    def func(self):
        self.value_2 = 20
ref_var = variables(10)
ref_var.func()
ref_var.value_3 = 30
print(ref_var.__dict__)

# Output
{'value_1': 10, 'value_2': 20, 'value_3': 30}
# instance variables inside the object without
# calling the method
class variables:
    def __init__(self, value_1):
        self.value_1 = value_1
    def func(self):
        self.value_2 = 20
ref_var = variables(10)
ref_var.value_3 = 30
print(ref_var.__dict__)

# Output
{'value_1': 10, 'value_3': 30}

Delete and update the instance variables

To delete the instance variables we can use the “del” keyword along with the variable name. If we are deleting the variable from inside of the class then we need to reference it using self.

If we are deleting it from outside of class then we need to reference it using the reference variable that is used to instantiate the class.

In the below example, we are deleting the value_2 from inside of a method( func ). Thus the method has to be called to execute the delete operation from the object.

class variables:
    def __init__(self):
        self.value_1 = 10
        self.value_2 = 20
        self.value_3 = 30
    def func(self):
        # deleting from inside of class
        del self.value_2
ref_var = variables()

# call the method
ref_var.func()

# deleting from outside of class
del ref_var.value_3
print(ref_var.__dict__)

# Output
{'value_1': 10}

Similarly to update the instance variable from inside of the class we need to refer to it using the self and if it’s outside of class then we need to refer to it using the reference variable.

class variables:
    def __init__(self):
        self.value_1 = 10
        self.value_2 = 20
        self.value_3 = 30
    def func(self):
        # updating from inside of class
        self.value_2 = 45
ref_var = variables()
print("before updating instance variables")
print(ref_var.__dict__)
# call the method
ref_var.func()

# updating from outside of class
ref_var.value_3 = 65
print("after updating instance variables")
print(ref_var.__dict__)

# Output
before updating instance variables
{'value_1': 10, 'value_2': 20, 'value_3': 30}
after updating instance variables
{'value_1': 10, 'value_2': 45, 'value_3': 65}

Class Variables

Class Variables are those that are defined inside a class and outside a method. These class variables are also known as static variables. These class variables can be used anywhere in the class and its methods, and by declaring the variable as global it can be used even outside of the class also. 

Unless the variable is not declared as private it is considered as public.

class class1:
    class_var1 = "hello python"
    static_var2 = "welcome"

class1_instance = class1()

print(class1_instance.class_var1, '\n', class1_instance.static_var2)

# Output
hello python 
welcome

We can access the class variables using the instance of the class. To use these class variables inside any method in the class we can use the instance of the class such as self.class_var1.

class class1:
    class_var1 = "hello python"
    static_var2 = "welcome"
    def func(self,):
        print(self.class_var1)

class1_instance = class1()
class1_instance.func()

# Output
hello python

Static Variables

Static variables are those that are defined inside a class and outside a method. These static variables are also known as class variables. The values of the static variables remain the same for every object created.

It means the copy of the static variables is shared by all the objects of the class, whereas each copy of the instance variables varies from one object to the other.

class tutorial:
    static_var1 = "hello"
    def __init__(self, inst_var_1):
        self.inst_var_1 = inst_var_1
   
obj_1 = tutorial(25)
obj_2 = tutorial(40)
print(obj_1.static_var1)
print(obj_2.static_var1)

# Output
hello
hello

These static variables can be used anywhere in the class and its methods, and by declaring the variable as global it can be used even outside of the class also. Unless the variable is not declared as private it is considered as public.

To access all the static variables of the class we need to use class_name.__dict__ which return a dictionary of key-value pairs with all the static variables.

class tutorial:
    static_var1 = "hello"
    static_var2 = "welcome"
    def func(self):
        print(self.static_var1, self.static_var2)
obj = tutorial()
obj.func()
print(obj_1.static_var1)
print(tutorial.__dict__)

# Output
hello
hello welcome
{'__module__': '__main__', 'static_var1': 'hello',
 'static_var2': 'welcome', 
 ---
 ---
 '__weakref__': <attribute '__weakref__' of 'tutorial' objects>,
 '__doc__': None}

Declaring a static variable

We can declare a static variable in different ways such as in a constructor of the class, instance method, static method of the class, also in a class method too.

  • If we declare a static variable in the class we can declare it directly
  • To declare a static variable in a constructor we need to declare it using classname.static_var
  • For declaring the static variable in a instance_method of the class we can use classname.static_var
  • For declaring the static variable in a class_method we need to use either classname or the reference variable(“cls”) of the class method. example:- classname.static_var or cls.
  • To declare the static variable in a static_method we need to declare it using classname.static_var
  • We have to make sure we have called these methods to reflect the respective static variables in the class object
class tutorial:
    static_var1 = "hello"
    def __init__(self):
        tutorial.static_var2 = "welcome"
    def func(self):
        tutorial.static_var3 = "John"
    @classmethod
    def cls_meth(cls):
        cls.static_var4 = "age"
        tutorial.static_var5 = 24
    @staticmethod
    def static_meth():
        tutorial.static_var6 = "bye"
obj = tutorial()
obj.func()
obj.cls_meth()
obj.static_meth()
print(tutorial.__dict__)

# Output
{'__module__': '__main__', 'static_var1': 'hello',
 ---
 'func': <function tutorial.func at 0x00000233EA666F70>, 
 'cls_meth': <classmethod object at 0x00000233EA5D4D90>, 
 ---
 'static_var2': 'welcome', 'static_var3': 'John', 
 'static_var4': 'age', 'static_var5': 24, 
 'static_var6': 'bye'}

Accessing a Static Variable

A static variable can be accessed in many ways such as inside of the class and also from outside of the class.

  • To access static variable from a constructor or instance method we can either use self or class_name
  • To access static variable from a class method we can either use cls or class_name
  • To access static variable from a static method we can use class_name
  • To access the static variable from outside of the class we can either use object reference or class_name
# Access static variable

class tutorial:
    static_var = "hello"
    def __init__(self):
        # accessing in a constructor
        print(self.static_var)
        print(tutorial.static_var)
    def func(self):
        # accessing in a instance method
        print(self.static_var)
        print(tutorial.static_var)
    @classmethod
    def cls_meth(cls):
        # accessing in a class method
        print(cls.static_var)
        print(tutorial.static_var)
    @staticmethod
    def static_meth():
        # accessing in a static method
        print(tutorial.static_var)
obj = tutorial()
obj.func()
obj.cls_meth()
obj.static_meth()
# accessing outside of class
print(obj.static_var, tutorial.static_var)

Note:- In a similar way we can update or delete the static variables in the respective methods. The approach for accessing the static variable plays a crucial role here. If we want to delete the static variable we can use the del keyword.

# updating and deleting the static variable

class tutorial:
    static_var = "hello"
    def __init__(self):
        # updating in a constructor
        print("updated value in constructor")
        tutorial.static_var = 20
        print(tutorial.static_var)
    @classmethod
    def cls_meth(cls):
        # updating in a class method
        cls.static_var = 30
        print("updated value in class method")
        print(cls.static_var)
    @staticmethod
    def static_meth():
        # deleting in a static method
        del tutorial.static_var
obj = tutorial()
obj.cls_meth()
obj.static_meth()

Local Variables

Local variables are those which are confined to a particular method only. These local variables are used on a temporary basis only to perform some operation in the particular method.

These local variables can be accessed from other methods by changing the scope of the variable to global.

class tutorial:
    @classmethod
    def cls_meth(cls):
        global a
        a = 39
    @staticmethod
    def static_meth():
        a = 40
        print(a)
obj = tutorial()
obj.cls_meth()
obj.static_meth()

Methods in Python

Methods are the functions that are bound to the object of the class and operate on the data of the object. Whenever an object is created for a class, a copy of all the methods and variables is created in the current object.

We always define a method inside the class and are called using object and dot operator.

There are different methods that are allowed in a class:-

  • Instance Method
  • Class Method
  • Static Method

Instance Method

If the method of a class uses any instance variables then that method is called the instance method. The first argument of the instance method has to be the reference variable(” self “) of the current object.

By using the reference variable we can access the instance variable values.

class tutorial:
    def __init__(self, inst_var_1, inst_var_2):
        self.inst_var_1 = inst_var_1
        self.inst_var_2 = inst_var_2
    # Instance method
    def func(self):
        print(self.inst_var_1)
        print(self.inst_var_2)

obj = tutorial("john", 45)
obj.func()

# Output
john
45

Class Method

Class methods are those that are bound to the class instead of being bound to the object of the class just like instance methods. It means class methods are in no way related to the object of the class.

Class methods do not use instance variables instead they use static variables.

For every class, the Python Virtual Machine creates a class object which maintains the class-level information(class variables, class methods, etc.,). The parameter of the class method is the reference variable that points to this class object.

We can use any variable as a reference variable in a class method but commonly most programmers use cls as a reference variable.

We have to declare these class methods using the @classmethod decorator. To call these class methods we can either use object reference or class name.

class tutorial:
    static_var_1 = "hello"
    static_var_2 = "welcome"

    # declaring class method
    @classmethod
    def func(cls):
        print(cls.static_var_1)
        print(cls.static_var_2)

obj = tutorial()

# calling class method in different ways
obj.func()
tutorial.func()

# Output
hello
welcome
hello
welcome

Static Method

Static Methods are those that do not use any instance variables or static variables. These static methods are also called general utility methods. Hence a static method does not need a reference variable as its parameter.

Static Methods can be declared using @staticmethod decorator. To access these static methods we can either use object reference or class name.

But it’s recommended to use a class name with a dot operator to call a static method because calling it using an object reference only creates one more copy in the object which is not useful.

class tutorial:
    static_var_1 = "hello"
    static_var_2 = "welcome"

    # declaring static method
    @staticmethod
    def func(var_1, var_2):
        print(var_1)
        print(var_2)

obj = tutorial()

# calling static method in different ways
obj.func(2, 5)
tutorial.func(7, 9)

# Output
2
5
7
9

Instance Method vs Class Method vs Static Method

  • Instance methods use instance variable inside them
  • Inside instance method we need to pass self as the first parameter
  • By using self we can access the instance variables
  • We can call the instance method using object reference
  • Class methods use static variables inside them
  • Class methods need to be declared using @classmethod decorator
  • We need to pass a reference variable that points to the class such as cls or any name
  • We can call class method using class name or object reference
  • Static methods do not use instance variables or static variables inside them
  • Static methods need to be declared using @staticmethod decorator
  • We do not have to pass any reference variable since they do not point to any object or class
  • We can call the static method using object reference

Access Members of One Class from Other Class

We can access members such as methods of one class from other classes. When an object is created all the class members of the class are loaded into the current object.

In the below example we are passing the current object of the employee class as a reference variable into the static method of the member class, thus we can access all the members of the object.

class employee:
    def __init__(self, name, age):
        self.name = name
        self.age = age
class member:
    def func(obj):
        print(obj.name)
        print(obj.age)
obj = employee("john", 25)
member.func(obj)

# Output
john
25

Nested Classes and Nested Methods

Declaring a class inside another class is called Nested class. Nested classes are used when there is a need for object dependency. When an object is dependent on another object then it is called object dependency.

When the object of one class is dependent on the object of another class then nested classes are used by using the object dependency concept.

  • Nested classes improve modularity by structuring one class inside the other class
  • Nested classes also provide security since the inner class can only be accessed if the object of the outer class is created

In the below example, when the object( obj_outer ) of the outer class is created it creates a copy of all the members of the outer class. Using the object( obj_outer ) of the outer class we can access the inner class using the dot operator.

After accessing the inner class we can create the object( obj_inner ) for the inner class and access its methods.

class outer:
    def __init__(self):
        print("outer class")
    class inner:
        def __init__(self):
            print("inner class")
obj_outer = outer()
obj_inner = obj_outer.inner()

# Output
outer class
inner class

Alternatively, we can access the inner class methods in a single line using the below code.

obj_outer = outer()
obj_outer.inner().func()

# output
outer class
inner class
inner func

We can use nested methods similar to nested classes. A method inside another method is known as a nested method.

Nested methods are used when there is a requirement for repeated usage of code. Then this piece of code can be placed in a method that can be inside another method and can be called any number of times.

class tutorial:
    def func(self):
        print("outer func")
        def func2(a, b):
            print("nested func", a + b)
        func2(2, 3)
obj = tutorial()
obj.func()

# Output
outer func
nested func 5