Objects and Object Oriented Programming
Published: 2023-05-11 06:15:02


### What is an Object and Class:
* Object can be any real life entity, that has some functionalities and attributes.
* Class is a blueprint that provides the functionality to the object
	* e.g:  __Car__ is a object which has functionalities like movement, transport etc.
	* The __Car__ comes under a __class__ of __Vehicles__
	* That means Vehicle will provide blueprint of functions that car has to perform

## Principles of OOP:
* OOP introduced as a representation of real world scenarios 
* It works majorly on four principles
	* __Inheritence__
	* __Abstraction__
	* __Polymorphism__
	* __Encapsulation__
* Let's look at them one by one.

### __Inheritence__:
* Inheritence is a useful concept which helps us to reuse our code
* There are three types of Inheritence in python
	* Single Inheritence
	* Multi-level Inheritence
	* Multiple Inheritence


### Single Inheritence:
Here There will be only one parent class, one child class.
```python

class Animal:
'''Parent class'''
	def __init__(self,color,legs):
	''' Constructor of parent class'''
		self.color = color
		self.legs = 4
	def sound(self):
	'''Instance method of parent class'''
		pass
class Dog(Animal):
	'''Dog class inheriting from Animal class'''
	def __init__(self,color,legs,sound):
		super().__init__(color,legs)
		self.sound = sound
	def sound(self):
		print(self.sound)
dog = Dog('black',4,'Boww')
dog.sound()

```
* In the above example sound method was overrided in child class.
* child class can access the methods of parent class
* super key word is used to call the immediate parent class constructor

### Multi-level Inheritence:
* Here there will be chain of class that are inheriting.
```python
class  Parent:  
	def  __init__(self,name): 
		self.name = name 
		
class  Child(Parent):  
	def  __init__(self,name,age): 
		Parent.__init__(self,name) 
		self.age = age

class  Grandchild(Child):  
	def  __init__(self,name,age,location): 
		Child.__init__(self,name,age) 
		self.location=location 

gc = Child('Yaswanth',22,'Andhra Pradesh')
print(gc.name(), gc.age(), gc.location())

```
* In the above example, grandchild is inheriting attribute of parent class
* child is ingeriting parent class attributes
* Point to be noted is that, It's __not__ Possible for parent class to access child class attributes or methods.

### Multiple Inheritence
* Python supports multiple inheritence.
* It involves inheriting methods or attributes of two or more classes at same time
```python
class A:
	def __init__(self):
		self.a = 'A class'
class B:
	def __init__(self):
		self.b = 'B class'
class C(A,B):
	def __init__(self):
		A.__init__()
		B.__init__()

c = C()
print(c.a , c.b)
		
``` 
* Here we can access A, B attributes with c object.


### Abstraction
*  Following use case is one of the useful case that i cam to know of Abstraction
* Let's say when you are writing a class that has three methods, you know the implimentation of first two methods.
* So we can impliment two methods and mention third method as abstract method so others can impliment the left out method by overriding it.
*  Abstract methods are declared in python by Importing ABC class from abc module as shown
```python
from abc import ABC,abstractmethod
class A:
	def method_one(self):
		print('method one')
	def method_two(self):
		print('method two')
	@abstractmethod
	def method_three(self):
		pass
class B(A):
	def method_three(self):
		print('method three implimented')
b= B()
b.method_three()
```
* Here method_three has been declared as a abstractmethod, so it has to be implimented to create object for B class


### Polymorphism
* ploymorphism means the existence in many forms
* Overriding function comes under polymorphism
* Operator overriding is an example of polymorphism
```python
class A:
	def method_one(self):
		print('method from class A')
class B(A):
	def method_one(self):
		print('method from class B')
a=A()
a.method_one()
b=B()
b.method_one()
```
* In the above example same method giving different result for different objects,hence polymorphism is achived

### Encapsulation
* Encapsulation refers to the scope of attributes and methods of class
* In Python, this can be of three types
	* Public
	* Private (start with double underscore '__')
	* Protected (start with single underscore '_')
```python
class A:
	def __init__(self):
		self._a = 'A class protected attribute'
		self.__b = 'A class private attribute'
		self.c = 'A class public attribute'
class B(A):
	def __init__(self):
		super().__init__()
b=B()
print(b.c) # output : 'A class public attribute'
print(b._a) # output : 'A class protected attribute'
print(b.__b) # output :AttributeError: 'B' object has no attribute '__b'

```
* In the above example, b.__b returned error because __b is a private attribute of class A
* protected attributes can be accessed through inheritence
* public attributes can be accessed any where
Note: Same encapsulation applies for the methods also


## References:
* [Python Docs](https://docs.python.org/3/tutorial/datastructures.html)
* [w3 Schools](https://www.w3schools.com/python/python_dictionaries.asp)
* [GFG](https://www.geeksforgeeks.org/abstract-classes-in-python/)