Object Oriented Programming in Python?

Python has been an object oriented programming language since its existence. Classes and objects are the two main building blocks of object oriented programming.
A class creates a new type of objects where objects are instances of the class.
Let’s create one of the simplest class,
Let just define an empty class.
#Define a class
class Vehicle():
   pass # An empty block

# Instantiating objects
v = Vehicle()
print(v)
<__main__.Vehicle object at 0x055DB9F0>
So first, we use class statement to create new class Vehicle, which is followed by an indented block of statements which form the body of the class. In our case, we have an empty block which is indicted using the pass statement.
Next, to use Vehicle class, we have created an object/instance of this class using the name of the class followed by a pair of parentheses. Then to confirm the object is created, we simply print it and get the information that we have an instance of the Vehicle class in the __main__ module.
Object(v) is an instance of a class. Each specific object is an instance of a particular class. We can create as many instances of a class and contain classes methods and properties.
All the methods and variables defined inside the class are accessible to its objects.
class Vehicle():
   def car(self):
      color = 'Red'
      return color

v = Vehicle()
print(v.car())
print(v)
Red
<__main__.Vehicle object at 0x05A59690>
In python, when we define any method in a class, we need to provide one default argument to any instances method, which is self. It means when we create an object from that class, that object itself will pass in that method.
Generally we don’t provide any argument(self) while calling the function, but the argument(self) is a must, whenever we define that function inside that class.
Let’s understand above concept using example.
class myClass():
   def myMethod(self):
      return 'Hello'

myInstance = myClass()
print(myInstance.myMethod())
Hello
So in above program, we define a class myClass and inside it we defined a method myMethod() and pass only one argument called self.
However, when we call the method through the instance of a class, we have not provided any argument to it. This is because whenever we call the method on an instance, the first argument itself is an instance of a class.
Let’s modify one line from above program-
def myMethod():
I just remove the argument (self) from my method (myMethod()). Now let’s run the program again and see what happened.
================ RESTART: C:\Python\Python361\oops_python.py ================
Traceback (most recent call last):
File "C:\Python\Python361\oops_python.py", line 18, in <module>
print(myInstance.myMethod())
TypeError: myMethod() takes 0 positional arguments but 1 was given
So its mandatory that your first argument to the method is a self.
These are object-specific attributes defined as parameters to the __init__ method. Each object can have different values for themselves.
Consider below example,
import random

class myClass():
def myMethod(self):
self.attrib1 = random.randint(1, 11)
self.attrib2 = random.randint(1,21)

myInst = myClass()
myInst.myMethod()

print(myInst.attrib1)
print(myInst.attrib2)
2
18
In above program, the “attrib1” and “attrib2” are the instance attributes.
A constructor is a particular type of method which is used to initialize the instance members of the class.
Constructors can be of two types−
Parameterized Constructor
Non-parameterized constructor
In python, “__init__” is a unique method associated with every python class.
Python calls it automatically for every object created from the class. Its purpose is to initialize the class attributes with user-supplied values.
It is called constructor in object-oriented programming.
class Employee:
   def __init__(self, name):
      self.name = name
   
   def display(self):
      print("The name of the employee is: ", self.name)
obj1 = Employee('Zack')
obj2 = Employee('Rajesh')
obj3 = Employee('Tashleem')

obj3.display()
obj1.display()
The name of the employee is: Tashleem
The name of the employee is: Zack
In above program, when we create an instance (obj1 & obj2), we pass the name argument and constructor will assign that argument to the instance attribute.
So when we call the display method on a particular instance, we will get the particular name.
Being python is oop in nature, it provides a way to restrict the access to methods and variables.
With data encapsulation in place, we can not directly modify the instance attribute by calling an attribute on the object. It will make our application vulnerable to hackers. However, we only change the instance attribute values by calling the specific method.
class Product:
   def __init__(self):
      self.__maxprice = 1000
      self.__minprice = 1

   def sellingPrice(self):
      print('Our product maximum price is: {}'.format(self.__maxprice))
      print('Our product minimum price is: {}'.format(self.__minprice))

   def productMaxPrice(self, price):
      self.__maxprice = price

def productMinPrice(self, price):
self.__minprice = price

prod1= Product()
prod1.sellingPrice()

prod1.__maxprice = 1500
prod1.sellingPrice()

prod1.__minprice = 10
prod1.sellingPrice()

prod1.productMaxPrice(1500)
prod1.sellingPrice()

prod1.productMinPrice(10)
prod1.sellingPrice()
Our product maximum price is: 1000
Our product minimum price is: 1
Our product maximum price is: 1000
Our product minimum price is: 1
Our product maximum price is: 1000
Our product minimum price is: 1
Our product maximum price is: 1500
Our product minimum price is: 1
Our product maximum price is: 1500
Our product minimum price is: 10
In above program, we have created an instance of Product class and try to modify the instance variable’s value, but it still gives the value which set inside the constructor.
Only way to modify the instance attribute’s value is by calling the productMaxPrice() or productMinPrice() method.