OOP CONCEPT IN PYTHON
Published: 2023-02-16 06:40:47

* Python is an object-oriented language.
* An object-oriented is to design the program using classes and objects.
* The main concept of OOP's is to bind the data and the functions that work on that together as a single unit so that no other part of the code can access this data.

## Main concepts of OOP's

### Class
* Class is defined as a collection of objects.
* It contains the blueprints of objects.
* Classes are created by keyword class.
* Attributes are the variables that belong to a class.
* Attributes are always public and can be accessed using dot(.) operator. Eg: Myclass.Application.

#### Syntax
```
class ClassName:
	# statement-1
	.
	.
	.
	# statement-2
```
 
#### Example
```
class Dog:
    pass
```

### Object
* The object is an entity that has state and behavior.
* A method is a function that belongs to an object.
* It may be any real-world object like a mouse, keyboard, chair, table, pen, etc.
* Everything in Python is an object, and almost everything has attributes and methods.
* State : It is represented by the attributes of an object and also reflects the properties of an object. 
* Behavior : It is represented by the methods of an object and also reflects the response of an object to other objects.
* Identity : It gives a unique name to an object and enables one object to interact with other objects.

#### self 
* It represents the instance of the class.
* It allows you to access variables, attributes and methods of a defined class.
* The self parameter doesn't have to be named “self,” as you can call it by any other name.

#### Example
```
class Dog:
	def __init__(self, breed, colour):
		self.breed = breed
		self.colour = colour
	def display(self):
		print(self.breed, self.colour)
d = Dog("Samoyed", "White")
d.display()
# output: Samoyed White
```

### Inheritance
* Inheritance is the capability of one class to derive or inherit the properties from another class. 
* It represents real-world relationships well.
* It provides the reusability of a code and it allows us to add more features to a class without modifying it.

### Types of Inheritance

#### 1.Single Inheritance
* It enables a derived class to inherit characteristics from a single-parent class.

#### 2.Multilevel Inheritance
* It enables a derived class to inherit properties from an immediate parent class which in turn inherits properties from his parent class.

#### 3.Hierarchical Inheritance
* It enables more than one derived class to inherit properties from a parent class.

#### 4.Multiple Inheritance
* It enables one derived class to inherit properties from more than one base class.

#### Syntax
```
class BaseClass:
	{body}
class DerivedClass(BaseClass):
	{body}
```

#### Example
```
class Person(object):
	def __init__(self, name):
		self.name = name
	def getName(self):
		return self.name
	def isEmp(Person):
		return False
class Employee(Person):
	def isEmployee(self):
		return True
per = Person("Abhi")
print(per.getName(), per.isEmployee())

per = Person("Visal")
print(per.getName(), per.isEmployee())
# output: Abhi False
          Visal False
```

### Polymorphism
* Polymorphism contains two words "poly" and "morphs". 
* It means having many forms.

#### Example
```
def add(x, y, z = 0):
	return x + y + z

print(add(2, 3))
print(add(2, 3, 4))
# output : 5
           9
```

#### Method Overriding
* It 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. 
* When a method in a subclass has the same name, same parameters or signature and same return type as a method in its super-class, then the method in the subclass is said to override the method in the super-class.

#### Example
```
class Parent():
    def __init__(self):
        self.value = "Inside Parent"
    def show(self):
        print(self.value)
class Child(Parent):
    def __init__(self):
        self.value = "Inside Child"
    def show(self):
        print(self.value)
          

obj1 = Parent()
obj2 = Child()
  
obj1.show()
obj2.show()
#output: Inside Parent
	 Inside Child
```

#### Method Overloading
* Two or more methods have the same name but different numbers of parameters or different types of parameters, or both.
* The problem with method overloading in Python is that we may overload the methods but can only use the latest defined method. 

#### Example
```
def product(a, b):
    p = a * b
    print(p)

def product(a, b, c):
    p = a * b * c
    print(p)

product(4, 5, 5)
#output: 100
```

### Encapsulation
* It describes the idea of wrapping data and the methods that work on data within one unit.
* This puts restrictions on accessing variables and methods directly and can prevent the accidental modification of data.
* Protected members(single underscore"_") are those members of the class that cannot be accessed outside the class but can be accessed from within the class and its subclasses.
* Private members(double underscore"__") are similar to protected members, the difference is that the class members declared private should neither be accessed outside the class nor by any base class.

#### Example
```
class Base:
	def __init__(self):
		self._a= 2
class Derived(Base):
	def __init__(self):
		Base.__init__(self)
		print("Base class: ", self._a)
		
		self._a = 3
		print("Outside class: ", self._a)
obj1 = Derived()
obj2 = Base()

print("Accessing obj1: ", obj1._a)
print("Accessing obj2: ", obj2._a)
#output: Base class:  2
	Outside class:  3
	Accessing obj1:  3
	Accessing obj2:  2
```

### Data Abstraction
* It hides the unnecessary code details from the user.
* It can be achieved through creating abstract classes.
* data hiding: We use double underscore("__") before the attributes names and those attributes will not be directly visible outside.
* Printing objects :Printing objects give us information about objects we are working with.

#### Example
```
class MyClass:
     __hiddenVariable = 0
     def add(self, increment):
        self.__hiddenVariable += increment
        print (self.__hiddenVariable)

myObject = MyClass()     
myObject.add(2)
myObject.add(5)

print (myObject.__hiddenVariable)
#output: 2
         7
Traceback (most recent call last):
  File "filename.py", line 13, in 
    print (myObject.__hiddenVariable)
AttributeError: MyClass instance has 
no attribute '__hiddenVariable' 
```

### Conclusion
In Python, Object-Oriented programming(OOP's) is a programming paradigm that uses objects and classes in programming. It aims to implement real-world entities like inheritance, polymorphism, encapsulation, etc. in the programming.


### References
* Reference links for OOP's concepts.

1.(https://www.geeksforgeeks.org/python-oops-concepts/)

2.(https://www.javatpoint.com/python-oops-concepts)