Shreeyantra day-2

What is a Global Variable?

A global variable is a variable that is declared outside of any function. This means it can be accessed and modified from anywhere in the program, including inside functions. In Python, when a variable is defined outside a function, it is considered a global variable.

Why Use Global Variables in This Task?

In this banking system, the account balance needs to be accessible and modifiable by different functions (deposit, withdraw, check_balance). If we were to define the balance inside each function, it would be a local variable, and the changes made to it would not be reflected outside that function. By using a global variable, the balance can be updated by one function and accessed by others.

How Does the Global Variable Work in This Example?

  1. Global Balance:

    • The global variable balance is initialized to 0.

    • It is accessed and modified in the deposit, withdraw, and check_balance functions using the global keyword.

  2. Modifying the Global Variable:

    • In each function, the global balance statement tells Python that we want to work with the global variable, not create a new local variable.

    • For example, when the deposit() function adds money, it updates the global balance to reflect the new total.

  3. Accessing the Global Variable:

    • The check_balance() function prints the current balance by accessing the global balance variable.

    • The withdraw() function also accesses and updates the global balance variable when money is withdrawn.

  4. Global Keyword:

    • The global keyword is essential to let Python know that we are referring to the global variable, otherwise Python would create a local variable within each function.

Key Points About Global Variables:

  • Scope: A global variable is accessible throughout the program, across all functions and blocks of code.

  • Modification: Functions can modify a global variable using the global keyword.

  • Best Practices: While global variables are useful, excessive use can make code harder to understand and maintain. They are often avoided in favor of passing values between functions using parameters or return values.

# Global variable to store the balance
balance = 0

# Function to deposit money into the account
def deposit(amount):
    global balance  # Access the global balance variable
    if amount > 0:  # If the amount is valid (positive)
        balance += amount
        print(f"Deposited {amount}. Current balance: {balance}")
    else:  # If the amount is invalid (non-positive)
        print("Deposit amount must be greater than 0!")

# Function to withdraw money from the account
def withdraw(amount):
    global balance  # Access the global balance variable
    if amount > 0:  # If the amount is valid (positive)
        if amount <= balance:  # If the amount is less than or equal to the balance
            balance -= amount
            print(f"Withdrew {amount}. Current balance: {balance}")
        else:  # If there are insufficient funds
            print("Insufficient funds!")
    else:  # If the amount is invalid (non-positive)
        print("Withdrawal amount must be greater than 0!")

# Function to check the balance
def check_balance():
    global balance  # Access the global balance variable
    print(f"Current balance: {balance}")

# Test the banking system with if-else logic
deposit(1000)      # Output: Deposited 1000. Current balance: 1000
withdraw(500)      # Output: Withdrew 500. Current balance: 500
withdraw(600)      # Output: Insufficient funds!
check_balance()    # Output: Current balance: 500
deposit(-200)     # Output: Deposit amount must be greater than 0!
withdraw(-100)    # Output: Withdrawal amount must be greater than 0!

Exception Handling in Python

Exception handling in Python is a mechanism that allows you to handle errors gracefully and prevent crashes in your program. It helps in debugging and ensures that the program continues to execute even when an error occurs.


1. What is an Exception?

An exception is an event that occurs during program execution and disrupts the normal flow of the program. Examples of exceptions include:

  • ZeroDivisionError – Division by zero

  • IndexError – Accessing an invalid index in a list

  • KeyError – Accessing a non-existent key in a dictionary

  • FileNotFoundError – Trying to open a file that does not exist

  • TypeError – Performing an invalid operation on data types


2. Handling Exceptions using try and except

To handle exceptions, Python provides the try-except block:

Basic Example

try:
    x = 10 / 0  # Division by zero
except ZeroDivisionError:
    print("Error: Cannot divide by zero!")

Output:

Error: Cannot divide by zero!

3. Handling Multiple Exceptions

You can catch multiple exceptions using multiple except blocks.

try:
    num = int(input("Enter a number: "))
    result = 10 / num
except ZeroDivisionError:
    print("Cannot divide by zero!")
except ValueError:
    print("Invalid input! Please enter a number.")

4. Catching All Exceptions (Exception class)

Instead of handling specific exceptions, you can catch all exceptions using the Exception class.

try:
    x = int("abc")  # Invalid conversion
except Exception as e:
    print(f"An error occurred: {e}")

5. Using else and finally

  • else Block – Runs only if there is no exception.

  • finally Block – Always executes, regardless of exceptions.

try:
    num = int(input("Enter a number: "))
    result = 10 / num
except ZeroDivisionError:
    print("Cannot divide by zero!")
else:
    print("Division successful:", result)
finally:
    print("Execution complete.")

In Python, file handling allows us to create, read, write, and append files. When working with files, the cursor (file pointer) plays a crucial role in determining where operations begin and end.


Opening a File and Cursor Position

To open a file, we use the open() function:

file = open("example.txt", "mode")
  • "example.txt" → Name of the file.

  • "mode" → Specifies how the file should be opened (read, write, append, etc.).

  • The cursor position depends on the mode used.


1. Writing to a File (w mode)

  • The cursor starts at the beginning of the file.

  • If the file already exists, it overwrites all content.

  • If the file does not exist, it creates a new file.

  • After writing, the cursor moves to the end of the file.

file = open("example.txt", "w")  # Cursor is at the beginning
file.write("Hello, World!")  # Cursor moves to the end after writing
file.close()

📌 Cursor Position:

  • Initially at the beginning.

  • Moves to the end after writing.


2. Appending to a File (a mode)

  • The cursor starts at the end of the file.

  • New content is added without overwriting existing content.

  • The cursor moves forward as new content is appended.

file = open("example.txt", "a")  # Cursor starts at the end
file.write("\nAppended text.")  # Cursor moves further after writing
file.close()

📌 Cursor Position:

  • Initially at the end of the file.

  • Moves further as new content is added.


3. Reading a File (r mode)

  • The cursor starts at the beginning.

  • As the file is read, the cursor moves forward.

  • After reading, the cursor is at the end of the file.

file = open("example.txt", "r")  # Cursor starts at the beginning
content = file.read()  # Cursor moves as content is read
print(content)
file.close()

📌 Cursor Position:

  • Initially at the beginning.

  • Moves to the end after reading.


4. Moving the Cursor with seek()

  • seek(n) moves the cursor to position n (starting from 0).

  • seek(0) moves the cursor back to the beginning.

file = open("example.txt", "r")
print(file.read())  # Cursor moves to the end
file.seek(0)  # Move cursor back to the beginning
print(file.read())  # Read again from the beginning
file.close()

📌 Cursor Behavior: 1️⃣ Starts at the beginning.
2️⃣ Moves to the end after reading.
3️⃣ seek(0) resets it to the beginning.


5. Reading Specific Characters with read(n)

  • Reads only n characters.

  • The cursor moves forward as characters are read.

file = open("example.txt", "r")
print(file.read(5))  # Reads first 5 characters, cursor moves
print(file.read(5))  # Reads next 5 characters
file.close()

📌 Cursor Behavior:

  • After reading 5 characters, the cursor moves forward by 5.

  • The next read(5) continues from where the cursor was left.


6. Best Practice: Using with Statement

  • The with statement automatically closes the file after execution.
with open("example.txt", "r") as file:
    content = file.read()
    print(content)  # Cursor moves to the end

📌 Cursor Position:

  • Starts at the beginning.

  • Moves to the end after reading.


Summary of Cursor Behavior

ModeInitial Cursor PositionCursor After Operation
wBeginningEnd (Overwrites file)
aEndEnd (Appends content)
rBeginningEnd (After reading)
seek(n)Moves cursor to position nCursor at n
def register():
    username = input("Enter your username")
    password = input("Enter your password")



    try:
        file = open(f"{username}.txt",'r')
        print("username already exist")

    except:
        with open(f"{username}.txt",'w') as f:
            f.write(password)


def login():
    username = input("Enter your username")
    password = input("Enter your password")


    try:
        file  = open(f"{username}.txt",'r')
    except:
        print("user not found\n")
        exit()



    with open(f'{username}.txt','r') as f:
        checkpass = f.readline()
        if password == checkpass:
            print("Welcome")
        else:
            print("sorry incorrect password")




def ask():
    x = input("do you want to register or login.. press r for register and l for login")
    if x == 'r':
        register()
    else:
        login()
ask()

Object-Oriented Programming (OOP)

OOP is a programming paradigm that uses objects and classes to organize code. It helps in creating modular and reusable code by bundling data and methods that operate on that data into objects.

1. Classes and Objects

  • Class: A blueprint for creating objects (instances). It defines the attributes and behaviors of the objects.

  • Object: An instance of a class. It represents a specific item created from the class.

Example:
class Car:
    def __init__(self, make, model):
        self.make = make  # Attribute
        self.model = model  # Attribute

    def display_info(self):
        print(f"{self.make} {self.model}")  # Method

# Create an object of the class
my_car = Car("Toyota", "Corolla")
my_car.display_info()  # Output: Toyota Corolla

2. Encapsulation

  • Encapsulation is the concept of bundling data (attributes) and methods (functions) into a single unit (class).

  • It also involves restricting access to certain components (attributes/methods) of the class using access modifiers (public, private).

Example:
class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.__salary = salary  # Private attribute

    def get_salary(self):
        return self.__salary  # Public method to access private attribute

emp = Employee("John", 50000)
print(emp.name)  # Accessible
print(emp.get_salary())  # Accessible via public method

3. Inheritance

  • Inheritance allows one class (child class) to inherit the attributes and methods of another class (parent class).

  • The child class can override or extend the behavior of the parent class.

Example:
class Animal:
    def speak(self):
        print("Animal makes a sound")

class Dog(Animal):  # Inheriting Animal class
    def speak(self):
        print("Dog barks")  # Overriding the parent class method

dog = Dog()
dog.speak()  # Output: Dog barks

Types of Inheritance in Python

Inheritance allows one class (child) to inherit attributes and methods from another class (parent). Python supports different types of inheritance:


1. Single Inheritance

  • A child class inherits from a single parent class.

  • The child class gets access to the methods and attributes of the parent class.

Example:

class Parent:
    def show(self):
        print("This is the Parent class")

class Child(Parent):  # Inheriting from Parent class
    def display(self):
        print("This is the Child class")

c = Child()
c.show()    # Output: This is the Parent class
c.display() # Output: This is the Child class

2. Multiple Inheritance

  • A child class inherits from more than one parent class.

  • The child class gets features from multiple parent classes.

Example:

class Father:
    def father_traits(self):
        print("Father's traits")

class Mother:
    def mother_traits(self):
        print("Mother's traits")

class Child(Father, Mother):  # Inheriting from two classes
    def child_traits(self):
        print("Child has traits from both parents")

c = Child()
c.father_traits() # Output: Father's traits
c.mother_traits() # Output: Mother's traits
c.child_traits()  # Output: Child has traits from both parents

Note: In case of conflicts, Python follows the Method Resolution Order (MRO) (left-to-right).


3. Multilevel Inheritance

  • A class inherits from another class, which itself is inherited from another class.

  • This creates a chain of inheritance.

Example:

class Grandparent:
    def grandparent_feature(self):
        print("Grandparent's feature")

class Parent(Grandparent):  # Inheriting from Grandparent
    def parent_feature(self):
        print("Parent's feature")

class Child(Parent):  # Inheriting from Parent
    def child_feature(self):
        print("Child's feature")

c = Child()
c.grandparent_feature()  # Output: Grandparent's feature
c.parent_feature()       # Output: Parent's feature
c.child_feature()        # Output: Child's feature

4. Hierarchical Inheritance

  • Multiple child classes inherit from a single parent class.

  • Each child class gets access to the parent's attributes and methods but remains independent of other siblings.

Example:

class Parent:
    def show(self):
        print("This is the Parent class")

class Child1(Parent):  # First child class
    def display1(self):
        print("This is Child1")

class Child2(Parent):  # Second child class
    def display2(self):
        print("This is Child2")

c1 = Child1()
c2 = Child2()

c1.show()     # Output: This is the Parent class
c1.display1() # Output: This is Child1

c2.show()     # Output: This is the Parent class
c2.display2() # Output: This is Child2

4. Polymorphism

  • Polymorphism allows different classes to have methods with the same name but different behaviors.

  • It allows objects of different classes to be treated as objects of a common superclass.

Example:
class Cat:
    def speak(self):
        print("Cat meows")

class Bird:
    def speak(self):
        print("Bird chirps")

def animal_sound(animal):
    animal.speak()  # Polymorphism: calling speak method for different classes

cat = Cat()
bird = Bird()

animal_sound(cat)  # Output: Cat meows
animal_sound(bird)  # Output: Bird chirps

Virtual Environment in Python

A virtual environment in Python is an isolated environment that allows you to install dependencies (packages) for a specific project without affecting the global Python environment.

It helps in: ✅ Managing dependencies for different projects separately.
✅ Avoiding conflicts between package versions.
✅ Keeping the system’s Python installation clean.


Steps to Create and Use a Virtual Environment

1. Install virtualenv (if not installed)

Python has a built-in module called venv, but you can also use virtualenv for more features.

python -m venv myenv

Using virtualenv (Alternative)

First, install it if not already installed:

pip install virtualenv

Then create a virtual environment:

virtualenv myenv

Note: Replace myenv with any name you prefer for your virtual environment.


2. Activate the Virtual Environment

After creating the virtual environment, you need to activate it.

On Windows (Command Prompt)

env\Scripts\activate

On Windows (PowerShell)

myenv\Scripts\Activate.ps1

On macOS/Linux

source myenv/bin/activate

Once activated, you will see the virtual environment name in the terminal prompt:

(myenv) $

3. Install Packages in the Virtual Environment

Now that the virtual environment is activated, you can install packages specific to this environment.

Example:

pip install django

To check installed packages:

pip list

4. Deactivate the Virtual Environment

When you are done working, deactivate the environment to return to the global Python environment.

deactivate

5. Delete a Virtual Environment (Optional)

If you no longer need a virtual environment, simply delete the folder.

On Windows/macOS/Linux:

rm -rf myenv  # Use 'rmdir /s /q myenv' on Windows Command Prompt

Using a Virtual Environment in a Project

If you want to use the same virtual environment for a project across different systems, follow these steps:

  1. Create a requirements.txt file

     pip freeze > requirements.txt
    

    This saves all installed packages.

  2. Install dependencies on another system

     pip install -r requirements.txt
    

    This installs all the required packages in a new virtual environment.


Summary

StepCommand
Create a virtual environmentpython -m venv myenv
Activate (Windows)myenv\Scripts\activate
Activate (macOS/Linux)source myenv/bin/activate
Install packagespip install package_name
Save installed packagespip freeze > requirements.txt
Install from requirements.txtpip install -r requirements.txt
Deactivatedeactivate
Delete virtual environmentrm -rf myenv