OOPS Concept

Python class attributes are variables of a class that are shared between all of its instances. They differ from instance attributes in that instance attributes are owned by one specific instance of the class and are not shared between instances.

An instance attribute is a Python variable belonging to one, and only one, object. This variable is only accessible in the scope of this object, and it’s defined inside the constructor function, __init__(self,..) of the class.


A class attribute is a Python variable that belongs to a class rather than a particular object. It’s shared between all the objects of this class and is defined outside the constructor function, __init__(self,...), of the class.

The method is a function that is associated with an object


Python is also an object-oriented programming language.it allows us to develop applications using object oriented approach.

OOPS Concept in Python:

  • Class
  • Objects
  • Polymorphism
  • Encapsulation
  • Inheritance
  • Data Abstraction
Class: It is a logical entity that contains some attribute and method. A class contains the blueprints or the prototype from which the objects are being created.

In other words, It is a user-defined data type that contains variables, data and methods. We can use the class as a template to create objects.

For example: Let's assume the class is a template for a building or a map of the building, we can create multiple buildings using this map or template and all buildings will be objects.

It contains fields, constructor, function,

How to Create Class  and Objects in Python:

Basic example:

class home:
    def room(w,d,r,k,wr):
        print(w,d,r,k,wr)
h=home()
h.room(6,5,2,1)

Constructor in Python:
It is a special type of method that is used to initialize the instance member of the class.

Types of Constructors:

1. Parameterized
2. Non Parameterized

Constructor Executes when we create an object of the class.

How to create a constructor in Python

 __init__() simulates the constructor of the class. This method is called when the class is instantiated.

It accepts the self keywords to use attributes and methods of the class.

We can pass any number of arguments at the time of creating the class object, depending upon the __init__() definition. 
It is mostly used to initialize the class attributes. Every class must have a constructor, even if it simply relies on the default constructor

Self is a reference to the current instance of the class. It is created and passed automatically/implicitly to the __init__() when the constructor is called..

class home:
    def __init__(self,w,d,r,k):
        self.r=r
        self.window=w
        self.kitchen=k
        self.door=d
    def room(self):
        print(self.r,self.window,self.kitchen,self.door)
      
h1=home(6,5,2,1)
h1.room()

Count the number of objects of the class

class student:
    count=0
    def __init__(self):
        student.count=student.count+1
s1=student()
s2=student()
s3=student()
print(student.count)

Parameterized Constructor: When the constructor accepts arguments along with self, it is known as a parameterized constructor.

Non- Parameterized Constructor: When the constructor doesn't accept any arguments from the object and has only one argument, self, in the constructor, it is known as a non-parameterized constructor.

class student:
    def __init__(self):
        self.c='gfjhfg'
    def count(self):
        print(self.c)
s=student()
s.count()

Default Constructor: When you do not write the constructor in the class created, Python itself creates a constructor during the compilation of the program.

The object of the class will always call the last constructor if the class has multiple constructors.


Objects: The object is an entity that has a state and behavior associated with it. it may be a real-world object like a mouse, keyboard, chair, table etc.. int, string, dictionary, array, and list are also an object. 12 is an object, 'hello' is an object. Lists, tuples, and dictionaries are objects which contain other objects

Polymorphism: Polymorphism simply means having many forms. one task can be performed in different ways. For example - you have a class animal, and all animals speak. But they speak differently. Here, the "speak" behavior is polymorphic in a sense and depends on the animal. So, the abstract "animal" concept does not actually "speak", but specific animals (like dogs and cats) have a concrete implementation of the action "speak".


Inheritance: Inheritance is the most important aspect of object-oriented programming, which simulates the real-world concept of inheritance. It specifies that the child object acquires all the properties and behaviors of the parent object.

Types of Inheritance

  • Single Inheritance: Single-level inheritance enables a derived class to inherit characteristics from a single-parent class.
class Animal:
    #attribute and method of parent class
    leg=''
    def eat(self):
        print('we do eat')
class dog(Animal):
    t='D'
    def display(self):
        print(self.leg)
d=dog()
d.eat()
d.leg='4'
d.display()    
     
    
  • Multilevel Inheritance: Multi-level inheritance enables a derived class to inherit properties from an immediate parent class which in turn inherits properties from his parent class. 
class Animal:
    #attribute and method of parent class
    leg=''
    def eat(self):
        print('we do eat')
class dog(Animal):
    t='D'
    def display1(self):
        print(self.leg)
class german_sp(dog):
    def display(self):
        print('german breed')
d=german_sp()
d.eat()
d.leg='4'
d.display() 
d.display1()  
    
 
  • Hierarchical Inheritance: Hierarchical-level inheritance enables more than one derived class to inherit properties from a parent class.
class Animal:
    #attribute and method of parent class
    leg=''
    def eat(self):
        print('we do eat')
class dog(Animal):
    t='D'
    def display1(self):
        print(self.leg)
class cat(Animal):
    def display(self):
        print('german breed')
c=cat()
d=dog()
d.eat()
d.leg='4'
c.display() 
c.eat()
d.display1()  
    

  • Multiple Inheritance: Multiple-level inheritance enables one derived class to inherit properties from more than one base class.
class Animal:
    #attribute and method of parent class
    leg=''
    def eat(self):
        print('we do eat')
class dog():
    t='D'
    def display1(self):
        print(self.leg,self.t)
class cat(Animal,dog):
    def display(self):
        print('german breed')
c=cat()
c.leg='4'
c.display() 
c.eat()
c.display1()  
    

Encapsulation: Combining the data and its related method into a class, which restricts direct access to variables and methods and helps to prevent unintended change in data.

 Private and public variables are two ways of controlling access to class variables.

Need of encapsulation in Python: 

It enables users to set variables to either read or write

Access modifier in python encapsulation: It is used to restrict the access of class data member and member functions outside the class.

Type of access modifier in python:

Public Access modifier: It is a default for member variables and a method for the class. the data member and method are accessible outside and inside the class.

Private Access modifier: Data members and method are accessible only within the class.
how to create: use double underscore prefix before name and methods.methods

class abc:
    def __init__(self,name,balance):
        name=name
        self.__balance=balance
    def deposit(self,amount):
        self.__balance=self.__balance+amount
        print(self.__balance)
    def withdraw(amount):
        __balance=__balance-amount
        print(__balance)
    def __showbalance():
        print(self.__balance)
a=abc('nimisha',5000)
a.deposit(500)
a.__showbalance() # it will throw error because we cann't access outside the class

Protected Access Modifier: Data member and method are accessible only within the class and the class that is inherited from the protected class.


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


    def display(self):
        print("Name:", self.name)
        print("Age:", self.age)
        print("ID:", self._id)



class Student(Person):
    def __init__(self, name, age, _id, marks):
        super().__init__(name, age, _id)
        self.marks = marks


    def display(self):
        super().display()
        print("Marks:", self.marks)
person=Person('john',12,23)
person.display()
s=Student('smith',23,1,123)
s.display()

example 2:

# super class 
class Shape: 
      
    # constructor 
    def __init__(self, length, breadth): 
        self._length = length
        self._breadth = breadth 
          
    # public member function 
    def displaySides(self): 
  
        # accessing protected data members 
        print("Length: ", self._length) 
        print("Breadth: ", self._breadth) 
  
  
# derived class 
class Rectangle(Shape): 
  
    # constructor 
    def __init__(self, length, breadth): 
  
        # Calling the constructor of
        # Super class
        Shape.__init__(self, length, breadth) 
          
    # public member function 
    def calculateArea(self): 
                      
        # accessing protected data members of super class 
        print("Area: ", self._length * self._breadth) 
                      
  
# creating objects of the 
# derived class         
obj = Rectangle(80, 50
  
# calling derived member 
# functions of the class
obj.displaySides()
  
# calling public member
# functions of the class 
obj.calculateArea() 

https://www.codingninjas.com/studio/library/what-is-encapsulation-in-python

Data Abstraction: It is a process in which data and function are defined in such a way that only details can be seen and unnecessary implementations are hidden.

How to achieve abstraction in python:

It can be achieved by having abstract classes and method.

Abstract class: It is a blueprint for other classes, it allows us to create a set of method that must be created in the the child classes. Methods.

Abstract class contains one or more abstract method.

Abstract Method: It is as method which has only a declaration but doesn't have an implementation.

Implement through Programming:

from abc import ABC abstractmethod

# we need to import ABC class and abstract methodabstract method from abc module

For example, all the animals have features like number of leg, type etc. so we will declare abstract class that will have leg and type method after that we will declare a subclass of animals that must contain the leg and type function. 

from abc import ABC,abstractmethod
class shape(ABC):
    @abstractmethod
    def area(self):
        pass
    def perimeter(self):
        pass
class square(shape):
    def area(self,l,b):
        print(l*b)
    def perimeter(self,l,b):
        print(2*(l+b))
s=square()
s.area(1,2)
s.perimeter(1,2)


Below code will throw error:

from abc import ABC,abstractmethod
class shape(ABC):
    @abstractmethod
    def area(self):
        pass
class square(shape):
    print('hjk')
s=square()
s.perimeter(1,2)


Note: Abstract class is not a concrete class, it cannot be instantiated. When we create an object for the abstract class it raises an error


Polymorphism: It means the use of a single type entity to present different types in different scenarios.

ex;
Polymorphism in + operator
Polymorphism in Function len()

Polymorphism in Class method: Python allows different classes to have a method with the same name.

class math:
    def __init__(self,name,marks):
        self.name=name
        self.marks=marks
    def info(self):
        print(self.name,self.marks)
class physics:
    def __init__(self,name,marks):
        self.name=name
        self.marks=marks
    def info(self):
        print(self.name,self.marks)      
m=math('math',80)
p=physics('physics',90)
for i in (m,p):
    i.info()
    
Polymorphism and inheritance:

class math:
    def __init__(self,name,marks):
        self.name=name
        self.marks=marks
    def info(self):
        print(self.name,self.marks)
    def other(self):
        print('parent method')
class physics(math):
    def __init__(self,name,marks):
        self.name=name
        self.marks=marks
    def info(self):
        print(self.name,self.marks)      

p=physics('physics',90)
p.info()
p.other()
    
Due to polymorphism Python automatically recognized that the info method for the object is overridden, so it uses the one defined in the child class. On the other hand, the other() method for object is not overridden, it is used from the parent shape.