Objects and Object Oriented Programming
Published: 2023-05-10 17:45:16

## Introduction
Object-oriented programming is a widely-used paradigm in software development. Python's object-oriented features include classes, objects, inheritance and polymorphism which allow for efficient and modular coding.

Everything in Python is an object. Using Pythom we can create classes and objects.

##  Classes
1. Class is like a blueprint that helps to create an object. Classes provide a means of bundling data and functionality together.  It's a collection of objects.
2. Class comprises of properties and behaviour.

	For eg:- Consider a class Vehicle, it might have properties or behaviour of Vehicles. Those properties might be size, fuel type, number of wheels, body type, speed etc.
3.  How to make a __Class__ in Python. We use __Class__ keyword.

```python
class Vehicle:
	def __init__(self):
			#constructor
	def method1(self, argument1, arg2 ....):
			#method code
	def method1(self, argument1, arg2 ....):
			#method code
```


## Objects
Objects are an instance of a class. With the help of objects we can access the method and functions of a class.

This may be any real-world object. 
For eg:- In case of a class Vehicle, we can have real world objects like Bicycle, Motor-Cycle, Cars, Trucks etc.

Creating an object __Car__ of __Vehicle__ class.
```python
	#Case 1
	car = Vehicle()
	#Case 2	
	cycle = Vehicle
	cycle.method1()
	
```
This creates a new instance of class Vehicle and assigns this object to local variable car.

1. Objects consists of __attributes/properties__.
2. It consists of __behaviour__ represented by the methods of an object.
3. __Identity__ gives a unique name to an object.

Eg:- Car is an identity which can have properties like bodyType (like SUV, Sedan), color, top speed.
Behaviour could be acceleration, vehicle Engine, vehicle transmission type.

## Inheritance
Inheritance is the capability of one class to derive or inherit the properties from another class. 
Inheritance is like a family tree where children inherit traits and characteristics from their parents. Similarly, in object-oriented programming, a new class can be created by inheriting the properties and methods of an existing class.

```python
	class Vehicle:
		def __init__():
			#constructor
		def method1():
			#method
	
	class Car(Vehicle):
		#child class of Vehicle
		#This derives all the attributes and methods of parent class
		def __init__(self):
			#constructor
```

Types of Inheritance:- 

**Single Inheritance**:  
Single-level inheritance enables a derived class to inherit characteristics from a single-parent class.
```python
# Base class
class  Animal: 
	def  __init__(self, name, species):
		 self.name = name self.species = species 
class  Cat(Animal): 
	def  __init__(self, name): 
		super().__init__(name, species="Cat") 
		self.sound = "Meow"

cat1 = Cat("Luna")
print(cat1.name, cat1.species, cat1.sound) 
# output: Luna Cat Meow
```

**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.
```python
class  Animal: 
	def  __init__(self, name, species): 
		self.name = name self.species = species 

class  Mammal(Animal): 
	def  __init__(self, name, species): 
		super().__init__(name, species) 
		self.is_mammal = True  
		
class  Cat(Mammal): 
	def  __init__(self, name): 
		super().__init__(name, species="Cat") 
		self.sound = "Meow"

cat1 = Cat("Luna")
print(cat1.name, cat1.species, cat1.sound, cat1.is_mammal) 
# output: Luna Cat Meow True
```

**Hierarchical Inheritance:**  
Hierarchical level inheritance enables more than one derived class to inherit properties from a parent class.
```python
# Hierarchical inheritance
# Base class
class Animal:
    def __init__(self, name, species):
        self.name = name
        self.species = species
        
#Derived class 1
class Cat(Animal):
    def __init__(self, name):
        super().__init__(name, species="Cat")
        self.sound = "Meow"

# Derived class 2
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name, species="Dog")
        self.sound = "Woof"

class Bird(Animal):
    def __init__(self, name):
        super().__init__(name, species="Bird")
        self.sound = "Tweet"
        
# Derived class 3
cat1 = Cat("Luna")
dog1 = Dog("Max")
bird1 = Bird("Tweety")

print(cat1.name, cat1.species, cat1.sound)  # output: Luna Cat Meow
print(dog1.name, dog1.species, dog1.sound)  # output: Max Dog Woof
print(bird1.name, bird1.species, bird1.sound)  # output: Tweety Bird Tweet

```

**Multiple Inheritance:**  
Multiple level inheritance enables one derived class to inherit properties from more than one base class.
```python
# multilevel inheritance

class  Animal: 
	def  __init__(self, name, species): 
		self.name = name 
		self.species = species 
class  Carnivore: 
	def  __init__(self): 
		self.is_carnivore = True  
	def  eat(self): 
		print("I eat meat.")

class  Cat(Animal, Carnivore): 
	def  __init__(self, name): 
		Animal.__init__(self, name, species="Cat") 
		Carnivore.__init__(self)  
		self.sound = "Meow"

cat1 = Cat("Luna")
print(cat1.name, cat1.species, cat1.sound, cat1.is_carnivore)
# output: Luna Cat Meow True
```

## Polymorphism
The word "polymorphism" means "many forms", and in programming it refers to methods/functions/operators with the same name that can be executed on many objects or classes.

```python
class Bird:
	def intro(self):
		print("There are many types of birds.")

	def flight(self):
		print("Most of the birds can fly but some cannot.")

class sparrow(Bird):
	def flight(self):
		print("Sparrows can fly.")

class ostrich(Bird):
	def flight(self):
		print("Ostriches cannot fly.")

obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()

obj_bird.flight()
obj_spr.flight()
obj_ost.flight()

Output:- 
Most of the birds can fly but some cannot.
Sparrows can fly.
Ostriches cannot fly.
```
## Encapsulation
Encapsulation is the practice of hiding the internal details of an object and only exposing a public interface that can be used to interact with the object.
This puts restrictions on accessing variables and methods directly and can prevent the accidental modification of data.

```python
class Animal:
    def __init__(self, name, species):
        self.__name = name
        self.__species = species

    def get_name(self):
        return self.__name

    def set_name(self, name):
        self.__name = name

cat1 = Animal("Luna", "Cat")
print(cat1.get_name())  # output: Luna
cat1.set_name("Mittens")
print(cat1.get_name())  # output: Mittens

```
We are using setters and getters to access variables. Thus, preventing accidental change.

## Abstraction
Abstraction is the practice of focusing on the essential features of an object and ignoring the irrelevant details. It involves creating abstract classes and interfaces that define a set of methods or properties that a class must implement, without specifying how they are implemented.

```python
from abc import ABC, abstractmethod

class Animal(ABC):
    def __init__(self, name, species):
        self.name = name
        self.species = species

    @abstractmethod
    def make_sound(self):
        pass

class Cat(Animal):
    def make_sound(self):
        print("Meow")

class Dog(Animal):
    def make_sound(self):
        print("Woof")

cat1 = Cat("Luna", "Cat")
dog1 = Dog("Max", "Dog")

cat1.make_sound()  # output: Meow
dog1.make_sound()  # output: Woof

```

## References
[GeeksforGeeks](https://www.geeksforgeeks.org/python-oops-concepts/)
[W3Schools](https://www.w3schools.com/python/python_classes.asp)
[freecodecamp](https://www.freecodecamp.org/news/crash-course-object-oriented-programming-in-python/)