Object Oriented Programming
Object Oriented Programming

Object Oriented Programming

Definition:

Object-Oriented Programming (OOP) in Python is a programming worldview that utilizes the idea of "objects" to structure code. Everything is considered to be an object in Python, and these objects can be instances of classes, which are blueprints for creating objects. OOP in Python depends on standards like embodiment, Inheritance, polymorphism, and reflection.

We are going to learn the following concepts

  • Objects and Classes
  • Inheritance
  • Types of inheritance
  • Encapsulation
  • Abstraction
  • Polymorphism
  • Exception Handling

Classes and Objects: Python is an object-oriented programming language, which means that everything in it is an object with its own properties and methods. In Python, each data type is an object that has been instantiated earlier by some class. If we want to create a data type of our own, we'll make a class for it and define its attributes.

Objects Methods A function inside a class that is related to an object is called a method.

Important: Self is the mandatory first parameter of a method, which can also be named anything (but using self is a convention).

The self-parameter is a reference to the current instance of the class and is used to access variables that belong to the class.

Example

?class student:
    Name = ""
    age =""

student1 = student()
student1.name = "Iqra"
student1.age = 24
print(f"Name of student:{student1.name}")
print(f"age of student:{student1.age}")
        

Class:

Class is a Blueprint of an object

Example

class House:
    def construchouse(self, rooms, launch, kichen):
        self.rooms = rooms
        self.launch = launch
        self.kitchen = kichen
    def print(self):
        print(f"Number of rooms:{self.rooms}")
        print(f"Number of launch:{self.launch}")
        print(f"Number of kitchen:{self.kitchen}")

house1 = House()
house1.construchouse(10,2,3)
house1.print()
        

In this house example, we can use class multiple times and change the value of objects we don’t need to make a new class to construct another house.

Inheritance:

Inheritance is a significant thought in object-oriented programming (OOP) that licenses you to make one more class considering an ongoing class. Inheritance empowers code reuse, advances a progressive design of classes, and permits you to make particular classes from additional overall ones.

Parent Class, Base Class, or Superclass: the class that you're inheriting from. It defines a set of attributes and methods that can be shared by its subclasses.

Child Class, Derived Class, or Subclass: the new class you're creating. It inherits properties and strategies from the base class and can likewise have its qualities and techniques.

class superclass:
    def __init__(self, name):
        self.name  = name

class baseclass(superclass):
    def hook(self, age):
        self.age = age
    def print1(self):
        print(f"this is name{self.name}, this is age{self.age}")

o1 = baseclass(name='iqra')
o1.hook(age=21)
o1.print1()

        

Types of Inheritance

In Python, there are a few sorts of inheritance, each filling various needs.

Single inheritance: A child class acquires from just a single parent class. ( As displayed in the above model.)

Multiple Inheritance: A child class acquires from more than one parent class.

Multilevel Inheritance: A parent class inherits from a child class, and this child class inherits from another class.

Hierarchical Inheritance:?Different young person classes are secure from a lone parent class.

Hybrid Inheritance: Half-and-half inheritance is a blend of at least two sorts of inheritance.

?You can see the previous example for simple inheritance

Multiple Inheritance:

class multiple:
    def derived(self, message):
        self.message = message
class base:
    def base1(self, message2):
        self.message2 = message2
class base2(multiple, base):
    def base3(self, message3):
        self.message3 = message3
    
    def print3(self):
        print(f"this is multiple class {self.message} this is base class {self.message2} this is base2 {self.message3}")

m = base2()
m.derived(message ="Hello from drived class")
m.base1(message2 = "Hello from base class")
m.base3(message3="Hello from base2")
m.print3()
        

Multilevel Inheritance:

class parent:
    def hook(self, employee):
        self.employee = employee
class child(parent):
    def hook2(self, age):
        self.age = age
class child2(child):
    def hook3(self, salary):
        self.salary = salary
    
    def print4(self):
       print(f"employee name is {self.employee} employee age {self.age} employee salary is {self.salary}")
object2=child2()
object2.hook(employee = "john")
object2.hook2(age = 23)
object2.hook3(salary = "70,000")
object2.print4()

        

Hierarchical Inheritance:

class Animal:
    def sound(self):
        pass

class Dog(Animal):
    def sound(self):
        print("Dog barks.")

class Cat(Animal):
    def sound(self):
        print("Cat meows.")

dog = Dog()
cat = Cat()

dog.sound()
cat.sound()
        

Hybrid inheritance:

class A:
    def display(self):
        print("This is class A.")      # --> Diamond Problem

class B(A):
    def display(self):
        print("This is class B.")

class C(A):
    def display(self):
        print("This is class C.")

class D(C, B):
    def displays(self):
        pass

d = D()
d.display()
d.display()
d.display()
d.displays()
        

Access Specifiers

In Python, access specifiers are utilized to control the permeability and openness of class credits and strategies.

Public: A class's public attributes and methods can be accessed from any location by default.

Private: Python utilizes a naming show to demonstrate private credits and techniques. Properties and techniques that begin with a twofold highlight (__) are viewed as private and ought not to be gotten to straightforwardly from outside the class.

Protected: Python utilizes a solitary highlight (_) toward the start of a trait or technique name to recommend that it's planned for interior use inside the class and its subclasses.

#This is the private class
class parent:
    def __privateclass(self):
        print("This is a private class")
o = parent()
o._parent__privateclass()

#This is protected method
class ProtectedExample:

    def __init__(self):
        self._protected_var = "This is a protected variable"

    def display(self):
        print(self._protected_var)

obj = ProtectedExample()
obj.display()                # Output: This is a protected variable
print(obj._protected_var)    # Output: This is a protected variable

        

Encapsulation

Encapsulation can be thought of as a way to wrap up your data and its associated actions in a secure container. It's like placing your information and the things you can do with it in a protective bubble. This bubble shields the inner workings of your data, making it easier for different parts of your code to interact with it in a clear and controlled manner.

?class Person:
    def __init__(self, name, age):
        # Private attributes using the convention of a single underscore
        self._name = name
        self._age = age

    # Getter methods to access private attributes
    def get_name(self):
        return self._name

    def get_age(self):
        return self._age

    # Setter methods to modify private attributes
    def set_name(self, new_name):
        if isinstance(new_name, str):
            self._name = new_name
        else:
            print("Error: Name must be a string.")

    def set_age(self, new_age):
        if isinstance(new_age, int) and 0 < new_age < 150:
            self._age = new_age
        else:
            print("Error: Age must be an integer between 1 and 150.")

# Creating an instance of the Person class
person = Person("John Doe", 30)

# Accessing private attributes using getter methods
print(f"Name: {person.get_name()}")  # Output: Name: John Doe
print(f"Age: {person.get_age()}")    # Output: Age: 30

# Modifying private attributes using setter methods
person.set_name("Jane Doe")
person.set_age(35)

# Accessing modified attributes
print(f"Updated Name: {person.get_name()}")  # Output: Updated Name: Jane Doe
print(f"Updated Age: {person.get_age()}")    # Output: Updated Age: 35

        

Properties and Getter/Setter

Properties and getters/setters are ideas in object-oriented programming that permit you to control how the credits (information) of a class are gotten to and changed. They give a method for typifying the connection with credits by adding custom rationale, approval, and calculations. In Python, properties and getters/setters are utilized to guarantee controlled access to credits, even though Python doesn't implement severe access control like in a few different dialects.

class Base:
    def __init__(self, age = 0):
         self.__age = age

    # getter method
    def get_age(self):
        return self.__age

    # setter method
    def set_age(self, x):
        self.__age = x

ali = Base()  #instantiation

# setting the age using setter
ali.set_age(21)   #call setter

# retrieving age using getter
print(ali.get_age())

#print(ali.__age)
        

Abstraction

Abstraction is working on complex reality by zeroing in on the fundamental angles and disregarding pointless subtleties.

You can say we hide the information from the user, which is not important.

For instance, while utilizing a cell phone, you communicate with the UI and functionalities it provides without expecting to comprehend the multifaceted, specialized subtleties of how it functions inside. Deliberation permits you to utilize the cell phone as an instrument without having to have a deep understanding of its inward parts and cycles.

Unique Class and Strategies

A theoretical class is a class that cannot be started up straightforwardly and is intended to act as an outline for different classes. It might contain both normal strategies with executions and dynamic techniques without executions.

Conceptual strategies are announced in the theoretical class; however, they miss the mark on substantial code. Unique strategies go about as placeholders and should be carried out by concrete (non-conceptual) subclasses.

In Python, the idea of unique classes and strategies isn't generally as unbending as in different dialects. We want to utilize module ABC for proclaiming these.

from abc import ABC, abstractmethod

class Shape(ABC):
    # defining an abstract method
    @abstractmethod
    def area(self):
        pass

    # defining a concrete method
    def name(self, name):
      self.name = name
      print(f"The shape is {name}")

# Subclass 'Circle' inherits from 'Shape'
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

# Subclass 'Rectangle' inherits from 'Shape'
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

# Attempting to create an instance of the abstract class 'Shape'
# This will result in a TypeError since abstract classes cannot be instantiated
#shape = Shape()  # Uncommenting this line will raise an error

# Creating instances of concrete subclasses
circle = Circle(5)
rectangle = Rectangle(4, 6)

# Using the 'area' method of the subclasses
print("Circle Area:", circle.area())       # Output: Circle Area: 78.53975
print("Rectangle Area:", rectangle.area())  # Output: Rectangle Area: 24
rectangle.name("rectangle")        

Polymorphism

"Polymorphism" signifies "more than one form," and in programming it alludes to strategies, capabilities, or administrators with the very name that can be executed on many articles or classes.

Polymorphism in Python, as in numerous other item-situated programming dialects, alludes to the capacity of various items to answer a similar strategy or capability in a manner that is well-defined for their singular kinds or classes. It permits objects of various classes to be treated as occurrences of a typical base class, giving a brought-together connection point to interfacing with objects of different sorts.

# Class Plymorphism
# {Polymorphic behaviour with Duck typing}
class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

class Duck:
    def speak(self):
        return "Quack!"

# A function that demonstrates polymorphic behavior
def animal_sound(animal):
    return animal.speak()
dog = Dog()
#dog.animal(speak)
print("Dog:",dog.speak())
animals = [Dog(), Cat(), Duck()]
for animal in animals:
    print("Sound:", animal_sound(animal))        

Raise an Exception

As a Python designer, you can decide to toss a special case on the off chance that a condition happens.

To toss (or raise) a special case, utilize the raise catchphrase.

age = int(input("Enter your age."))
if age<18:
  raise Exception("Sorry minors can't apply for this.")
else:
  print(age)

# Raising a built-in exception
def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

result = divide(10, 0)  # Attempt to divide by zero  --> Arithmatic exception        

Custom Exception

Custom Exception: You can make your custom special cases by characterizing new classes that are acquired from the Exception class or its subclasses. This permits you to characterize exceptions that are well-defined for your application requirements.

# Define a custom exception class
class MyCustomError(Exception):
    def __init__(self, message):
        super().__init__(message)

# Function that raises the custom exception
def validate_age(age):
    if age < 0:
        raise MyCustomError("Age cannot be negative")

user_age = int(input("Enter your age: "))
validate_age(user_age)
print(f"Your age is: {user_age}")        

Exception Handling

An exception in Python is a component that permits you to nimbly deal with mistakes or outstanding circumstances that might happen during the execution of a program. Rather than allowing these special cases to crash the program, Python gives a method for getting and handling them, permitting the program to proceed with its execution or make a proper move.

Key concepts and components of exception handling in Python include:

  • try block lets you test a block of code for errors.
  • except block lets you handle the error.
  • else block lets you execute code when there is no error.
  • Finally lets you execute code regardless of the result of the try-and-except blocks.

# Exception type
try:
    x = int(input("Enter a number: "))
    result = 10 / x
    name = input("Enter your name: ")
    print("Hello, " + name)
except ValueError:
    # Handle ValueError (e.g., if the user doesn't enter a valid integer)
    print("Invalid input for number")
except ZeroDivisionError:
    # Handle ZeroDivisionError (e.g., if the user enters 0)
    print("Cannot divide by zero")
except Exception as e:
    # Handle any other exception not caught by the previous blocks
    print(f"An error occurred: {e}")


# else
try:
  abc = 5
  print(abc)
except:
  print("An error occured.")
else:
  print("Code executed without any error!")

# finally block
try:
  ab = 5
  if ab<4:
   print(ab)
except:
  print("An error occured.")
else:
  print("Code executed without any error!")
finally:
  print("Program ended.")        


Patrick Nicolas

Director Data Engineering @ aidéo technologies |software & data engineering, operations, and machine learning.

10 个月

Very useful.. I would add static method (for class level implementation), class method & __new__ operators for alternative constructors..

Jaweria Batool

Software Engineer || MLSA - β || MERN Stack Developer || Artificial Intelligence || Technical Writer || Moderator @ICodeGuru || Python

10 个月

This sounds insightful!

回复
Haris Ellahi

Software Engineer | 3X International Hackathon winner | Advent Of Code 2023 Participant | Python Devotee | Machine Learning | AI | REACT JS | Tailwind CSS | PHP | Laravel | MySQL

10 个月

Very useful

回复

要查看或添加评论,请登录

社区洞察

其他会员也浏览了