Classes and Objects in Python- Object Oriented Programming & A Project

by | Jul 10, 2024 | Python | 0 comments

In Python, classes and objects are the fundamental building blocks of object-oriented programming (OOP). A class defines a blueprint for objects, and objects are instances of a class. Here’s a detailed explanation along with examples to illustrate the concepts of classes and objects in Python.

  • Classes and Objects: Define a class using the class keyword. Create objects by instantiating the class.
  • Attributes and Methods: Attributes store data, and methods define behaviors.
  • Inheritance: Define a class that inherits from another class to reuse code.
  • Special Methods: Use special methods to define behavior for built-in operations.
  • Encapsulation: Restrict access to data by using private and protected attributes.

You Know what all data types in Python are implemented as classes. This means that every data type in Python, including integers, strings, lists, and dictionaries, is an instance of a class. This approach allows for a consistent and flexible handling of data and enables powerful object-oriented features such as inheritance and polymorphism.

Basic Data Types as Classes

Here’s a brief overview of how some basic data types are defined using classes in Python:

1. Integer (int)

In Python, integers are instances of the int class. The int class provides various methods for working with integer values.

# Example: Integer data type
x = 5
print(type(x))  # Output: <class 'int'>

# Integer methods
print(x.bit_length())  # Output: 3 (binary representation of 5 is 101)

2. String (str)

Strings in Python are instances of the str class. The str class provides numerous methods for string manipulation.

# Example: String data type
s = "Hello, World!"
print(type(s))  # Output: <class 'str'>

# String methods
print(s.upper())  # Output: "HELLO, WORLD!"
print(s.lower())  # Output: "hello, world!"
print(s.split(','))  # Output: ['Hello', ' World!']

3. List (list)

Lists in Python are instances of the list class. The list class provides methods for list manipulation, such as appending, extending, and sorting elements.

# Example: List data type
lst = [1, 2, 3, 4, 5]
print(type(lst))  # Output: <class 'list'>

# List methods
lst.append(6)
print(lst)  # Output: [1, 2, 3, 4, 5, 6]
lst.reverse()
print(lst)  # Output: [6, 5, 4, 3, 2, 1]

4. Dictionary (dict)

Dictionaries in Python are instances of the dict class. The dict class provides methods for dictionary manipulation, such as adding, removing, and accessing key-value pairs.

# Example: Dictionary data type
d = {'a': 1, 'b': 2, 'c': 3}
print(type(d))  # Output: <class 'dict'>

# Dictionary methods
print(d.keys())  # Output: dict_keys(['a', 'b', 'c'])
print(d.values())  # Output: dict_values([1, 2, 3])
print(d.items())  # Output: dict_items([('a', 1), ('b', 2), ('c', 3)])

Let us first fix our steps so that no confusion on how to proceed on this topic.

Creating a class and proceeding with object-oriented programming (OOP) in Python involves several steps:

Step1- Define the Class: Use the class keyword followed by the class name.

Inside the class, define attributes (data) and methods (functions). Inside the class, an __init__ method has to be defined with def. This is the initializer that you can later use to instantiate objects. It’s similar to a constructor in Java. __init__ must always be present! It takes one argument: self, which refers to the object itself. Inside the method, the pass keyword is used as of now, because Python expects you to type something there. 

class MyClass:
    def __init__(self, attribute1, attribute2):
        self.attribute1 = attribute1
        self.attribute2 = attribute2
    
    def method1(self):
        # method definition
        pass
    
    def method2(self):
        # method definition
        pass

Step2 Instantiate Objects:

Create instances (objects) of the class by calling the class name followed by parentheses.

This invokes the __init__ method (constructor) to initialize the object.

obj1 = MyClass('value1', 'value2')

Step3- Access Attributes and Call Methods: Use dot notation (obj.attribute or obj.method()) to access attributes and call methods of the object.

print(obj1.attribute1) 
obj1.method1()

Step4- Define and Call Class Methods: Methods within the class can operate on the instance (self) and class itself (cls).

class MyClass:
    @classmethod
    def class_method(cls):
        # class method definition
        pass
    
    @staticmethod
    def static_method():
        # static method definition
        pass

Step5- Inheritance: Create a subclass that inherits from a superclass using parentheses after the class name.

class SubClass(MyClass):
    def __init__(self, attribute1, attribute2, attribute3):
        super().__init__(attribute1, attribute2)
        self.attribute3 = attribute3

Step6- Polymorphism: Objects of different classes can be treated as objects of a common superclass, allowing for interchangeable usage.

Step7Encapsulation: Use access modifiers (public, private, protected) to control the visibility and accessibility of attributes and methods.


Classes and Objects in Python

Defining a Class

A class is defined using the class keyword followed by the class name and a colon. Inside the class, you can define methods (functions) and attributes (variables).

class Car:
    # Class attribute
    wheels = 4

    # Initializer / Instance attributes
    def __init__(self, make, model, year):
        self.make = make
        self.model = model
        self.year = year

    # Instance method
    def description(self):
        return f"{self.year} {self.make} {self.model}"

    # Another instance method
    def start_engine(self):
        return f"The engine of {self.make} {self.model} is now running!"

Creating Objects

Objects are instances of a class. You create an object by calling the class name followed by parentheses.

# Creating instances of the Car class
car1 = Car("Toyota", "Corolla", 2020)
car2 = Car("Honda", "Civic", 2019)

# Accessing attributes and methods
print(car1.description())  # Output: 2020 Toyota Corolla
print(car2.start_engine()) # Output: The engine of Honda Civic is now running!

# Accessing class attribute
print(f"All cars have {car1.wheels} wheels.")  # Output: All cars have 4 wheels.

Detailed Example

Let’s create a more detailed example to demonstrate additional concepts like inheritance, method overriding, and special methods.

Defining a Base Class and a Derived Class

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

    def speak(self):
        raise NotImplementedError("Subclass must implement this method")

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # Call the initializer of the base class
        self.breed = breed

    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name)  # Call the initializer of the base class
        self.color = color

    def speak(self):
        return f"{self.name} says Meow!"

Creating Objects and Using Inheritance

dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers", "Tabby")

print(dog.speak())  # Output: Buddy says Woof!
print(cat.speak())  # Output: Whiskers says Meow!

Inheritance

Imagine you’re building a restaurant management system using Python’s object-oriented programming features. Here’s how inheritance can help:

Base Class – MenuItem:

class MenuItem:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def get_details(self):
        return f"{self.name} - ${self.price}"

This base class represents a generic menu item with attributes name and price and a method get_details that returns a formatted string description.

Derived Class – Beverage (Inheriting from MenuItem):

class Beverage(MenuItem):
    def __init__(self, name, price, size):
        super().__init__(name, price)  # Call base class constructor
        self.size = size

    def get_details(self):  # Override base class method
        return f"{self.size} {self.name} - ${self.price}"

  • Beverage inherits from MenuItem.
  • It adds an additional attribute size and overrides the get_details method to include the size information.
  • super().__init__(name, price) calls the constructor of the base class to initialize the inherited attributes.

Derived Class – Food (Inheriting from MenuItem):

class Food(MenuItem):
    def __init__(self, name, price, vegetarian):
        super().__init__(name, price)  # Call base class constructor
        self.vegetarian = vegetarian

    def is_vegetarian(self):
        return self.vegetarian

    def get_details(self):  # Override base class method
        veg_info = "Vegetarian" if self.vegetarian else "Non-Vegetarian"
        return f"{self.name} ({veg_info}) - ${self.price}"

  • Food inherits from MenuItem.
  • It adds an additional attribute vegetarian and a method is_vegetarian to check the dietary category.
  • It overrides the get_details method to include vegetarian information.

Using the Classes:

coffee = Beverage("Coffee", 2.5, "Small")
pizza = Food("Margherita Pizza", 12.99, False)
salad = Food("Greek Salad", 8.50, True)

print(coffee.get_details())  # Output: Small Coffee - $2.5
print(pizza.get_details())  # Output: Margherita Pizza (Non-Vegetarian) - $12.99
print(salad.is_vegetarian())  # Output: True

Benefits of Inheritance:

  • Code Reuse: We reuse the functionalities (attributes and methods) of MenuItem in Beverage and Food, reducing code redundancy.
  • Polymorphism: Each derived class can customize the get_details method to present information specific to its type (beverage size, food type).
  • Extensibility: We can easily create new types of menu items (e.g., Dessert) by inheriting from MenuItem and adding specific attributes and methods.

Special Methods

Special methods in Python are defined with double underscores before and after their names (e.g., __init__). These methods enable you to define behaviors for built-in operations.

Example of Special Methods

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(2, 3)
v2 = Vector(5, 7)
v3 = v1 + v2

print(v3)  # Output: Vector(7, 10)

Encapsulation

Encapsulation is the bundling of data (attributes) and methods that operate on the data into a single unit (class), and restricting access to some of the object’s components. This is achieved using private and protected attributes.

Example of Encapsulation

class BankAccount:
    def __init__(self, account_holder, balance):
        self.account_holder = account_holder
        self.__balance = balance  # Private attribute

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount

    def withdraw(self, amount):
        if amount > 0 and amount <= self.__balance:
            self.__balance -= amount

    def get_balance(self):
        return self.__balance

account = BankAccount("John Doe", 1000)
account.deposit(500)
account.withdraw(200)

print(account.get_balance())  # Output: 1300
# print(account.__balance)  # AttributeError: 'BankAccount' object has no attribute '__balance'

Understanding Polymorphism

Polymorphism means “having many forms.” In Python, it refers to the ability of objects of different classes to respond to the same method call in different ways. This flexibility promotes code reusability and maintains readability.

1. Duck Typing:

  • Python relies on duck typing, which means objects are used based on the methods they have, not necessarily their class.
  • This allows for flexibility in using objects as long as they provide the required functionality.

Example: File I/O Operations

def read_data(file_object):
    """Reads data from a file-like object."""
    return file_object.read()

text_file = open("data.txt", "r")
data = read_data(text_file)  # Can also work with a custom FileWrapper class

class FileWrapper:
    def __init__(self, data):
        self.data = data

    def read(self):
        return self.data

custom_data = FileWrapper("Custom Data")
custom_data_read = read_data(custom_data)  # Both read() functions are utilized

  • The read_data function doesn’t care about the specific class of the object as long as it has a read() method.
  • This allows us to use both the text_file object and the FileWrapper object with the same function.

2. Method Overriding:

  • In inheritance, subclasses can override methods inherited from the parent class to provide their own implementation.
  • This allows for specialization of behavior based on the object’s type.

Example: Shape Area Calculations

class Shape:
    def __init__(self):
        pass

    def area(self):
        raise NotImplementedError("Subclasses must implement area()")

class Rectangle(Shape):
    def __init__(self, length, width):
        self.length = length
        self.width = width

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

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

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

shapes = [Rectangle(5, 3), Circle(4)]
for shape in shapes:
    print(f"{shape.__class__.__name__} area: {shape.area()}")

  • The base class Shape defines an abstract area() method that raises an error.
  • Derived classes (Rectangle and Circle) override area() with their specific area calculation logic.
  • The loop iterates through objects of different classes, but the area() method is called polymorphically, resulting in the correct calculation based on the object’s type.

Benefits of Polymorphism:

  • Code Reusability: We can write functions that work with a variety of objects as long as they provide the required functionality.
  • Readability: Polymorphism promotes cleaner and more readable code by focusing on what objects can do (methods) rather than their specific type.
  • Extensibility: We can easily add new classes with specialized behaviors without modifying existing code.

How classes and Objects in Python is different from C++

Classes and objects in Python and C++ both follow the principles of object-oriented programming (OOP), but there are some key differences in their implementation and features due to the nature of the languages. Below, I will highlight the main differences and provide examples to illustrate these points.

Key Differences Between Python and C++ Classes and Objects

  1. Syntax and Simplicity:
    • Python: Python has a simpler and more readable syntax.
    • C++: C++ is more verbose and requires explicit declaration of data types and access specifiers.
  2. Memory Management:
    • Python: Python handles memory management automatically with garbage collection.
    • C++: C++ requires manual memory management, using constructors, destructors, and the new and delete operators.
  3. Access Specifiers:
    • Python: By default, all members are public. Private members are indicated by prefixing with a single underscore _ (convention) or double underscores __ (name mangling).
    • C++: Explicit access specifiers public, private, and protected are used.
  4. Inheritance and Polymorphism:
    • Python: Supports multiple inheritance directly.
    • C++: Supports multiple inheritance but with more complexity and potential issues like the diamond problem, which can be managed using virtual inheritance.
  5. Operator Overloading:
    • Python: Supports operator overloading, but it is less common and usually simpler.
    • C++: Extensively uses operator overloading, which is more complex.
  6. Virtual Functions and Abstract Classes:
    • Python: Uses abstract base classes (ABC) from the abc module to define abstract methods.
    • C++: Uses virtual functions and pure virtual functions to create abstract classes.

Examples to Illustrate Differences

Python Example

# Python Class Example
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

dog = Dog("Buddy")
cat = Cat("Kitty")

print(dog.speak())  # Output: Buddy says Woof!
print(cat.speak())  # Output: Kitty says Meow!

C++ Example

// C++ Class Example
#include <iostream>
#include <string>

class Animal {
public:
    Animal(std::string n) : name(n) {}
    virtual ~Animal() {}

    virtual std::string speak() const = 0; // Pure virtual function

protected:
    std::string name;
};

class Dog : public Animal {
public:
    Dog(std::string n) : Animal(n) {}

    std::string speak() const override {
        return name + " says Woof!";
    }
};

class Cat : public Animal {
public:
    Cat(std::string n) : Animal(n) {}

    std::string speak() const override {
        return name + " says Meow!";
    }
};

int main() {
    Dog dog("Buddy");
    Cat cat("Kitty");

    std::cout << dog.speak() << std::endl; // Output: Buddy says Woof!
    std::cout << cat.speak() << std::endl; // Output: Kitty says Meow!

    return 0;
}

Detailed Comparison

  1. Class Definitions:
    • In Python, classes are defined using the class keyword, and methods are defined within the class using def.
    • In C++, classes are defined using the class keyword, and methods are defined within the class scope.
  2. Constructors and Destructors:
    • Python uses the __init__ method for initialization.
    • C++ uses constructors with the same name as the class and destructors using the ~ symbol.
  3. Inheritance:
    • Python supports multiple inheritance directly by listing parent classes in parentheses.
    • C++ supports multiple inheritance but requires careful handling to avoid issues like the diamond problem.
  4. Abstract Methods:
    • In Python, abstract methods are defined using the abc module.
    • In C++, abstract methods are defined using pure virtual functions.
  5. Method Overloading and Overriding:
    • Python does not support method overloading (functions with the same name but different parameters) but supports method overriding.
    • C++ supports both method overloading and overriding.
  6. Access Control:
    • Python uses conventions (_ and __) for access control, but all members are technically public.
    • C++ uses public, private, and protected keywords for strict access control.

In Python, memory usage in object-oriented programming (OOP) revolves around how objects, classes, and their attributes are stored and managed. Here’s an explanation with an example:

Memory Usage in Python OOPs

  1. Classes and Objects:
    • Classes: Classes themselves are objects in Python, consuming memory to store their methods and attributes.
    • Objects (Instances): Each instance (object) of a class consumes memory to store its attributes.
  2. Attributes:
    • Each attribute (variable) within an object consumes memory space based on its type and value.
  3. Methods:
    • Methods defined within a class are stored in memory once per class, regardless of how many instances (objects) are created.
  4. Memory Management:
    • Python’s memory management (handled by the interpreter and the underlying CPython implementation) includes automatic garbage collection (reference counting and cyclic garbage collector) to reclaim memory from objects that are no longer referenced.

Example:

Let’s create a simple example to demonstrate memory usage:

class Person:
    def __init__(self, name, age):
        self.name = name   # String attribute
        self.age = age     # Integer attribute
    
    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

# Creating instances of the Person class
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)

# Accessing attributes and calling methods
print(person1.name)   # Output: Alice
print(person2.age)    # Output: 25

print(person1.greet())   # Output: Hello, my name is Alice and I am 30 years old.
print(person2.greet())   # Output: Hello, my name is Bob and I am 25 years old.

Explanation:

  • Class Definition:
    • The Person class defines two attributes (name and age) and one method (greet).
    • The __init__ method initializes each instance (self.name = name, self.age = age).
  • Instance Creation:
    • person1 and person2 are instances of the Person class, each occupying memory to store its attributes (name and age).
  • Memory Usage:
    • Memory is allocated for each instance (person1, person2) to store their respective name and age.
    • The greet method is stored once in memory for the Person class and shared among all instances.
  • Garbage Collection:
    • Python’s garbage collector automatically reclaims memory from objects that are no longer referenced (e.g., if person1 and person2 are no longer referenced).

In Python OOP, memory is used to store class definitions, attributes, and methods. Instances (objects) consume memory to hold their attributes, while methods are stored once per class. Python’s memory management handles allocation and deallocation of memory, ensuring efficient use and cleanup of resources.

what is __Main__ module. what are saved here?

The __main__ module in Python refers to the module that serves as the entry point to your program when it is run directly. It is a special module name that Python assigns to the script that is executed in the context of the main program. Here’s what you should know about it:

Purpose of __main__

  1. Entry Point: When you run a Python script directly using python script.py, Python sets the __name__ variable for that script to "__main__". This indicates that the script is being run as the main program.
  2. Module Context:
    • Any code inside the script that is not within a function or class definition is executed when the script is run as __main__.
    • It provides a way to structure Python programs so that certain code only runs when the script is executed directly, not when it is imported as a module into another script.

What is Saved in __main__

  • Global Variables: Variables defined at the top level of the script (not inside any function or class) are saved in the __main__ module.
  • Function Definitions: Functions defined at the top level of the script are part of the __main__ module.
  • Executable Code: Any executable code outside of function and class definitions is part of the __main__ module and executes when the script is run directly.

Example:

Consider a simple Python script example.py:

# example.py

def hello():
    return "Hello, World!"

print("This is executed in __main__")

if __name__ == "__main__":
    print("Running directly")
    result = hello()
    print(result)
  • When you run python example.py:
    • "This is executed in __main__" and "Running directly" are printed because they are in the __main__ module.
    • The hello() function is called and its result "Hello, World!" is printed.
  • If you import example.py as a module in another script:
    • The code inside if __name__ == "__main__": block does not execute.
    • You can still import and use the functions and variables defined in example.py.

The __main__ module in Python is where the code execution begins when a script is run directly. It includes global variables, function definitions, and executable code that are not within function or class definitions. Understanding __main__ helps you structure your Python programs to handle both standalone execution and module import scenarios effectively.


Example Project: Analyzing Real-Life Data with Python Classes

Project: Employee Management System

  1. Class Definition: Define classes for Employee, Manager, and Department.
  2. Attributes and Methods: Load and store employee data, calculate salaries, and manage departments.
  3. Inheritance: Use inheritance to create specialized employee types.
Python Implementation
import pandas as pd

class Employee:
    def __init__(self, emp_id, name, base_salary):
        self.emp_id = emp_id
        self.name = name
        self.base_salary = base_salary

    def get_annual_salary(self):
        return self.base_salary * 12

class Manager(Employee):
    def __init__(self, emp_id, name, base_salary, bonus):
        super().__init__(emp_id, name, base_salary)
        self.bonus = bonus

    def get_annual_salary(self):
        return super().get_annual_salary() + self.bonus

class Department:
    def __init__(self, name):
        self.name = name
        self.employees = []

    def add_employee(self, employee):
        self.employees.append(employee)

    def get_total_salary(self):
        return sum(emp.get_annual_salary() for emp in self.employees)

# Load employee data
data = {
    'emp_id': [1, 2, 3],
    'name': ['Alice', 'Bob', 'Charlie'],
    'base_salary': [5000, 7000, 6000],
    'bonus': [2000, 0, 1500]
}
df = pd.DataFrame(data)

# Create department and add employees
dept = Department('HR')
for index, row in df.iterrows():
    if row['bonus'] > 0:
        emp = Manager(row['emp_id'], row['name'], row['base_salary'], row['bonus'])
    else:
        emp = Employee(row['emp_id'], row['name'], row['base_salary'])
    dept.add_employee(emp)

# Calculate total salary
print(f"Total Salary in {dept.name} Department: {dept.get_total_salary()}")
  • Python: Easier syntax, automatic memory management, dynamic typing, and simpler inheritance model.
  • C++: More control over memory management, explicit typing, more complex but powerful features like operator overloading, virtual functions, and precise access control.

Each language has its strengths and is chosen based on the requirements of the project. Python is often preferred for rapid development and ease of use, while C++ is chosen for performance-critical applications and fine-grained control over system resources.

Project:-

Statement-to create a Banking Data universe ,using OOPs in Python Bank will be a class and different banks will be object. Want to add a new bank in universe will create a new object. Sub classes can be Retail Banking, Wholesome Banking, Deposits etc. Please create a architecture and python script for it. also please think and include DBMS , Big data framework and other operation to be includded in this framework.

Sample-

To create a comprehensive banking data universe using object-oriented programming (OOP) in Python, we need to define a well-structured class architecture that can encapsulate various banking operations. Here’s an approach to achieve this:

  1. Class Definitions:
    • Bank: Base class for all banks.
    • RetailBanking: Subclass for retail banking operations.
    • WholesaleBanking: Subclass for wholesale banking operations.
    • Deposits: Subclass for deposit operations.
  2. Database and Big Data Integration:
    • Integration with a relational database (e.g., MySQL, PostgreSQL) to store structured data.
    • Integration with a Big Data framework (e.g., Hadoop, Spark) for large-scale data processing.

Class Architecture

  1. Bank Class:
    • Attributes: bank name, ID, and other general details.
    • Methods: add bank, get bank details, etc.
  2. RetailBanking Class:
    • Attributes: specific to retail banking.
    • Methods: personal loans, savings accounts, etc.
  3. WholesaleBanking Class:
    • Attributes: specific to wholesale banking.
    • Methods: corporate loans, trade finance, etc.
  4. Deposits Class:
    • Attributes: deposit types, rates, etc.
    • Methods: create deposit, get deposit details, etc.

Example Python Script

class Bank:
    bank_count = 0
    
    def __init__(self, name):
        self.id = Bank.bank_count
        self.name = name
        self.retail_banking = None
        self.wholesale_banking = None
        self.deposits = None
        Bank.bank_count += 1
    
    def add_retail_banking(self):
        self.retail_banking = RetailBanking(self)
    
    def add_wholesale_banking(self):
        self.wholesale_banking = WholesaleBanking(self)
    
    def add_deposits(self):
        self.deposits = Deposits(self)
    
    def get_details(self):
        details = {
            'ID': self.id,
            'Name': self.name,
            'Retail Banking': self.retail_banking is not None,
            'Wholesale Banking': self.wholesale_banking is not None,
            'Deposits': self.deposits is not None
        }
        return details

class RetailBanking:
    def __init__(self, bank):
        self.bank = bank
        self.services = ['Personal Loans', 'Savings Accounts']
    
    def get_services(self):
        return self.services

class WholesaleBanking:
    def __init__(self, bank):
        self.bank = bank
        self.services = ['Corporate Loans', 'Trade Finance']
    
    def get_services(self):
        return self.services

class Deposits:
    def __init__(self, bank):
        self.bank = bank
        self.types = ['Fixed Deposit', 'Recurring Deposit']
    
    def get_deposit_types(self):
        return self.types

# Adding a new bank to the universe
bank_universe = []

def add_bank(name):
    bank = Bank(name)
    bank.add_retail_banking()
    bank.add_wholesale_banking()
    bank.add_deposits()
    bank_universe.append(bank)
    return bank

# Example of usage
hdfc_bank = add_bank("HDFC Bank")
icici_bank = add_bank("ICICI Bank")

for bank in bank_universe:
    print(bank.get_details())

# Output
# {'ID': 0, 'Name': 'HDFC Bank', 'Retail Banking': True, 'Wholesale Banking': True, 'Deposits': True}
# {'ID': 1, 'Name': 'ICICI Bank', 'Retail Banking': True, 'Wholesale Banking': True, 'Deposits': True}

Integration with DBMS and Big Data Framework

To integrate with a database and a big data framework, you can use libraries such as SQLAlchemy for database operations and PySpark for big data processing.

Database Integration Example

from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# Setting up the database
Base = declarative_base()
engine = create_engine('sqlite:///banking_universe.db')
Session = sessionmaker(bind=engine)
session = Session()

# Defining the Bank model
class BankModel(Base):
    __tablename__ = 'banks'
    id = Column(Integer, Sequence('bank_id_seq'), primary_key=True)
    name = Column(String(50))

# Creating the table
Base.metadata.create_all(engine)

# Adding banks to the database
def add_bank_to_db(name):
    bank = BankModel(name=name)
    session.add(bank)
    session.commit()

# Example of adding a bank to the database
add_bank_to_db("HDFC Bank")
add_bank_to_db("ICICI Bank")

# Querying the database
for bank in session.query(BankModel).all():
    print(bank.name)

Big Data Framework Integration Example

from pyspark.sql import SparkSession

# Initialize Spark session
spark = SparkSession.builder \
    .appName("BankingDataUniverse") \
    .getOrCreate()

# Sample data
data = [
    ("HDFC Bank", "Retail", 1000),
    ("ICICI Bank", "Wholesale", 2000)
]

# Creating DataFrame
columns = ["BankName", "BankType", "Transactions"]
df = spark.createDataFrame(data, columns)

# Performing operations
df.show()
df.groupBy("BankType").sum("Transactions").show()

# Stop the Spark session
spark.stop()

This architecture and implementation provide a foundation for a banking data universe using OOP in Python. It includes:

  • Class Structure: Bank, RetailBanking, WholesaleBanking, and Deposits.
  • DBMS Integration: Using SQLAlchemy for database operations.
  • Big Data Framework Integration: Using PySpark for large-scale data processing.

This setup can be expanded and customized based on specific requirements, such as adding more methods and functionalities to handle complex banking operations, and integrating with other technologies as needed.

Other Approach involving Metaclass can be:-

Creating a comprehensive banking data universe using OOP in Python involves designing a flexible architecture that includes classes for banks, various banking operations, and data management. The architecture can incorporate DBMS for data storage, a big data framework for handling large datasets, and metaclasses for managing the overall structure.

Architecture

  1. Banking Universe:
    • A metaclass to manage all banks.
    • A base Bank class.
    • Subclasses for different types of banking operations (Retail Banking, Wholesale Banking, Deposits, etc.).
    • Integration with DBMS and Big Data frameworks for data management and operations.
  2. Components:
    • Metaclass: BankingMeta to handle bank registration.
    • Base Class: Bank.
    • Subclasses: RetailBanking, WholesaleBanking, Deposits, etc.
    • Database Integration: Use SQLite for DBMS.
    • Big Data Integration: Placeholder for integrating with frameworks like Hadoop/Spark.

Python Script

import sqlite3

# Metaclass to manage all banks
class BankingMeta(type):
    _banks = {}

    def __init__(cls, name, bases, attrs):
        super().__init__(name, bases, attrs)
        if not cls.__name__ == 'Bank':
            BankingMeta._banks[cls.__name__] = cls

    @classmethod
    def get_bank(cls, name):
        return cls._banks.get(name)

    @classmethod
    def register_bank(cls, bank_name, bank_obj):
        cls._banks[bank_name] = bank_obj

# Base Bank class
class Bank(metaclass=BankingMeta):
    def __init__(self, name):
        self.name = name
        self.connect_db()

    def connect_db(self):
        self.conn = sqlite3.connect(f'{self.name}.db')
        self.cursor = self.conn.cursor()
        self.setup_db()

    def setup_db(self):
        raise NotImplementedError

    def close_db(self):
        self.conn.close()

# Subclass for Retail Banking
class RetailBanking(Bank):
    def setup_db(self):
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS retail_accounts (
                account_id INTEGER PRIMARY KEY,
                account_holder TEXT,
                balance REAL
            )
        ''')
        self.conn.commit()

    def add_account(self, account_holder, balance):
        self.cursor.execute('''
            INSERT INTO retail_accounts (account_holder, balance)
            VALUES (?, ?)
        ''', (account_holder, balance))
        self.conn.commit()

# Subclass for Wholesale Banking
class WholesaleBanking(Bank):
    def setup_db(self):
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS wholesale_accounts (
                account_id INTEGER PRIMARY KEY,
                company_name TEXT,
                credit_limit REAL
            )
        ''')
        self.conn.commit()

    def add_account(self, company_name, credit_limit):
        self.cursor.execute('''
            INSERT INTO wholesale_accounts (company_name, credit_limit)
            VALUES (?, ?)
        ''', (company_name, credit_limit))
        self.conn.commit()

# Subclass for Deposits
class Deposits(Bank):
    def setup_db(self):
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS deposits (
                deposit_id INTEGER PRIMARY KEY,
                depositor TEXT,
                amount REAL
            )
        ''')
        self.conn.commit()

    def add_deposit(self, depositor, amount):
        self.cursor.execute('''
            INSERT INTO deposits (depositor, amount)
            VALUES (?, ?)
        ''', (depositor, amount))
        self.conn.commit()

# Function to add a new bank
def add_new_bank(bank_type, bank_name):
    bank_class = BankingMeta.get_bank(bank_type)
    if bank_class:
        bank_obj = bank_class(bank_name)
        BankingMeta.register_bank(bank_name, bank_obj)
        print(f"New bank '{bank_name}' of type '{bank_type}' added to the universe.")
    else:
        print(f"Bank type '{bank_type}' not recognized.")

# Usage example
if __name__ == "__main__":
    add_new_bank("RetailBanking", "RetailBank1")
    add_new_bank("WholesaleBanking", "WholesaleBank1")
    add_new_bank("Deposits", "DepositBank1")

    # Interact with the banks
    retail_bank = BankingMeta.get_bank("RetailBank1")
    if retail_bank:
        retail_bank.add_account("John Doe", 1000.00)

    wholesale_bank = BankingMeta.get_bank("WholesaleBank1")
    if wholesale_bank:
        wholesale_bank.add_account("ABC Corp", 50000.00)

    deposit_bank = BankingMeta.get_bank("DepositBank1")
    if deposit_bank:
        deposit_bank.add_deposit("Jane Doe", 5000.00)

Explanation

  1. Metaclass BankingMeta:
    • Manages all bank classes and their instances.
    • Provides methods to get and register banks.
  2. Base Class Bank:
    • Defines the basic structure for a bank, including connecting to a database.
    • Uses SQLite for simplicity.
  3. Subclasses (RetailBanking, WholesaleBanking, Deposits):
    • Each subclass implements specific banking operations.
    • Each subclass has its own method to set up the database schema and add data.
  4. Function add_new_bank:
    • Adds new bank instances based on the bank type and name.
    • Registers the new bank in the banking universe.
  5. Usage Example:
    • Demonstrates how to add new banks and interact with them.

Future Enhancements

  1. Big Data Integration:
    • Integrate with Hadoop or Spark for large-scale data processing.
    • Use PySpark or similar frameworks to handle big data operations.
  2. Advanced DBMS:
    • Replace SQLite with more advanced DBMS like PostgreSQL or MySQL.
    • Implement advanced querying and data manipulation.
  3. Additional Features:
    • Implement more banking operations and subclasses.
    • Add functionalities for transactions, loans, interest calculations, etc.
    • Enhance security and user authentication mechanisms.

This architecture provides a flexible and scalable foundation for creating a comprehensive banking data universe in Python. By leveraging OOP principles, it ensures maintainability and ease of extension for future enhancements.

Written by HintsToday Team

Related Posts

Python Project Alert:- Dynamic list of variables Creation

Let us go through the Project requirement:- 1.Let us create One or Multiple dynamic lists of variables and save it in dictionary or Array or other datastructre for further repeating use in python. Variable names are in form of dynamic names for example Month_202401 to...

Data Structures in Python: Linked Lists

Linked lists are a fundamental linear data structure where elements (nodes) are not stored contiguously in memory. Each node contains data and a reference (pointer) to the next node in the list, forming a chain-like structure. This dynamic allocation offers advantages...

Python Dictionary in detail

What is Dictionary in Python? First of All it is not sequential like Lists. It is a non-sequential, unordered, redundant and mutable collection as key:value pairs. Keys are always unique but values need not be unique. You use the key to access the corresponding value....

Lists and Tuples in Python – Explained with loads of examples

What is List? Lists are a fundamental data structure in Python used to store collections of items. They are ordered, meaning elements have a defined sequence, and mutable, allowing you to modify their contents after creation. They are denoted by square brackets [ ],...

Python Strings Interview Questions

Python Programming Strings Interview Questions Write a Python program to remove a Specific character from string? Here's a Python program to remove a specific character from a string: def remove_char(text, char): """ Removes a specific character from a string. Args:...

Python ALL Eyes on Strings- String Data Type & For Loop Combined

It is a case sensitive, non-mutable sequence of characters marked under quotation. It can contain alphabets, digits, white spaces and special characters. In Python, a string is a sequence of characters enclosed within either single quotes (' '), double quotes (" "),...

Get the latest news

Subscribe to our Newsletter

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *