Python is an object-oriented programming language.

Classes can be defined in python using the class keyword.

A class creates a new local namespace where all its attributes are defined. Attributes may be data or functions.

There are also special attributes in it that begins with double underscores (__).

The first string is called the docstring and has a brief description about the class.
It is optional.

class newclass:
    "This is a docstring"
    pass


Example:

class MyClass: 
    "This is my second class" 
    def func ( self ) : 
        print ( ' Hello ' ) 

ob = MyClass() 
print ( ob.__doc__ )
ob. func() 

Output:

This is my second class
Hello

You may have noticed the self-parameter in function definition inside the class but, we called the method simply as ob.func() without any arguments. 
It still worked. 
This is because, whenever an object calls its method, the object itself is passed as the first argument. 
So, ob. func() translates into MyClass.func( ob ).


Constructors in python:

__init__() function is used to define constructors.
This special function gets called whenever a new object of that class is instantiated.
We normally use it to initialize all the variables

class st: 
    def __init__ ( self ) : 
        print ( “ Constructor Called ” ) 

v=st() 

OUTPUT: Constructor Called
    

#Program to use Parameterized Constructor 
class st: 
    def __init__ ( self  , n ) : 
        print ( “ Hello ” , n ) 

v = st ( “ Rishi ” ) 

OUTPUT : Hello Rishi



Inheritance in python

Inheritance allows us to define a class that inherits all the methods and properties from another class.

Parent class is the class being inherited from, also called base class. 
Child class is the class that inherits from another class, also called derived class


Single Inheritance:

Example:

class base:
    def basefunc( self ) :
        print ( "this is the base class " )
class derived ( base ) :
    def derivedfunc( self ) :
        print( "this is the derived class " )
obj = derived()
obj.basefunc()
obj.derivedfunc()

Output:

this is the base class 
this is the derived class 


Multi - level Inheritance:

Example:

class base:
    def basefunc( self ) :
        print ( "this is the base class " )
class derived1 ( base ) :
    def derived1func( self ) :
        print( "this is the derived class 1 " )
class derived2 ( derived1 ) :
    def derived2func( self ) :
        print( "this is the derived class 2 " )
obj = derived2()
obj.basefunc()
obj.derived1func()
obj.derived2func()

Output:

this is the base class 
this is the derived class 1 
this is the derived class 2


Multiple Inheritance:

Example:

class base1:
    def base1func ( self ) :
        print ( "this is the base class 1 " )
class base2 () :
    def base2func ( self ) :
        print( "this is the base class 2 " )
class derived ( base1 , base2 ) :
    def derivedfunc( self ) :
        print( "this is the derived class 2 " )
obj = derived()
obj.base1func()
obj.base2func()
obj.derivedfunc()

Output:

this is the base class 1 
this is the base class 2 
this is the derived class 2 



Hierarchical Inheritance:

Example:

class base:
    def basefunc ( self ) :
        print ( "this is the base class " )
class derived1 ( base ) :
    def derived1func ( self ) :
        print( "this is the derived class 1 " )
class derived2 ( base ) :
    def derived2func ( self ) :
        print( "this is the derived class 2 " )
        
obj = derived1()
obj.basefunc()
obj.derived1func()

obj = derived2()
obj.basefunc()
obj.derived2func()

Output:

this is the base class 
this is the derived class 1 
this is the base class 
this is the derived class 2 


Hybrid Inheritance:

It is a combination of more than one types of inheritance in one single program.


Method Overriding:

The method overriding allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its super classes or parent classes. 
One reason for overriding parents’ methods is because you may want special or different functionality in your subclass.

Example:

class T:
    def show(shelf):
        print ("Base class method called")
class S(T):
    def show(shelf):
        print ("Derive class method")
v=S ()
v.show ()
v1=T ()
v1.show()


Output:

Derive class method
Base class method called


Data Encapsulation:

Data encapsulation is used to restrict access to methods and variables. This can prevent the data from being modified by accident and is known as encapsulation

Example:

class st:
    def __init__(self):
        self._show1()
    def show(self):
        print (" How are you ? ")
    def __show1(self): #Private Function
        print("Hello")
v=st ()
v.show()
v.__show1() #Not accessible from object v because of private method


Output:

Hello 
How are U? 
ERROR