FUNCTIONS, PYTHON OOPS AND EXCEPTION HANDLING

FUNCTIONS, PYTHON OOPS AND EXCEPTION HANDLING


Functions

  • A function is a block of statements used to perform a specific task.
  • Functions allow us to divide a larger problem into smaller subparts to solve efficiently, to implement the code reusability concept and makes the code easier to read and understand. 
Types of Functions in Python:

1. Built-in Functions

These are the functions that come pre-defined with Python. They help perform common tasks like printing, taking input, working with numbers or sequences, etc.

Examples:
Function Description Example
print() Displays output on the screen print("Hello")
len() Returns the length of an object len("Python")
type() Returns the type of an object type(5)
int() Converts to integer int("10")
float() Converts to float float("3.14")
str() Converts to string str(100)
input() Takes input from the user input("Enter name: ")
sum() Returns the sum of elements in an iterable sum([1, 2, 3])
max() Returns the largest item max([4, 10, 2])
min() Returns the smallest item min([4, 10, 2])
abs() Returns absolute value abs(-7)
round() Rounds a number round(3.456, 2)
sorted() Returns a sorted list sorted([3, 1, 2])
range() Generates a sequence of numbers range(1, 5)
enumerate() Adds counter to an iterable enumerate(['a', 'b'])
zip() Combines iterables into tuples zip([1,2], ['a','b'])
isinstance() Checks data type isinstance(5, int)
id() Returns memory address of an object id(x)
help() Displays help/documentation help(len)

2. User-defined Functions

These are functions that you create to perform a specific task or set of tasks. They make your code modular, reusable, and easier to manage.

Syntax:

def function_name(arguments):
    # function body
    return result

Here,
  • def - keyword used to declare a function
  • function_name - any name given to function
  • arguments - any value passed to function
  • return (optional) - returns value from function
Example:

def greet(name):
    print(f"Hello, {name}!")

greet("Swathi")

Output:
Hello, Swathi!

Sum of 2 numbers with and without return:

def add_with_return(a, b):
    return a + b
# Using the return
result = add_with_return(5, 3)
print("Sum:", result)

Output:
Sum: 8

def add_without_return(a, b):
    print("Sum:", a + b)

add_without_return(5, 3)

Output:
Sum: 8


Function arguments:

Function arguments are the values you pass to a function when calling it. These values are assigned to the function’s parameters and used inside the function.
 
1. Positional Arguments

    The positional arguments are the arguments passed to a function in the same positional order as they defined in the function definition. Here, the number of arguments and order of arguments in the function call should exactly match with the respective function definition. If any mismatch leads to error. The positional arguments are also known as required arguments.

Example:
def greet(name, age):
    print(f"Hello {name}, you are {age} years old.")

greet("Swathi", 24)

 Output:
Hello Swathi, you are 24 years old.

2. Default Arguments

    The default argument is an argument which is set with a default value in the function definition. If the function is called with value then, the function executed with provided value, otherwise, it executed with the default value given in the function definition.

Example:
def greet(name="Swathi"):
    print(f"Hello {name}")

greet()         # Uses default
greet("Anu")    # Overrides default

Output:
Hello Swathi
Hello Anu

3. Keyword Arguments

The keyword argument is an argument passed as a value along with the parameter name (parameter_name = value). When keyword arguments are used, we may ignore the order of arguments. We may pass the arguments in any order because the Python interpreter uses the keyword provided to match with the respective parameter.

Example:
def greet(name, city):
    print(f"{name} is from {city}.")

greet(name="Swathi", city="Hyderabad")

Output:
Swathi is from Hyderabad.

4. Variable length keywords

Some times we may not aware of the number of arguments to be passed to a function definition, or it may change according to the situation. The Python provides variable-length of arguments which enable us to pass an arbitrary number of arguments. Here, all the arguments are stored as a tuple of parameters in the function definition. And they are accessed using the index values (similar to a tuple).

a. *args → Accepts multiple positional arguments.

Example:
def hobbies(*args):
    print("Swathi's hobbies are:")
    for hobby in args:
        print("-", hobby)

hobbies("Reading", "Painting", "Music")

Output:
Swathi's hobbies are:
- Reading
- Painting
- Music

b. **kwargs → Accepts multiple keyword arguments.
Example:
def profile(**kwargs):
    print("Swathi's Profile:")
    for key, value in kwargs.items():
        print(f"{key}: {value}")

profile(age=22, city="Hyderabad", skill="Python")

Output:
Swathi's Profile:
age: 22
city: Hyderabad
skill: Python

Scope of variables

Variable scope refers to where in your code a variable is accessible. The scope refers to the accessibility of a variable or object in the program. The scope of a variable determines the part of the program in which it can be accessed or used. In simple terms, the scope of a variable is the region of the program in which that variable can be accessed.
Python has the following types of variable scopes:

1. Local Scope
A variable declared inside a function is local to that function.

def greet():
    name = "Swathi"  # local variable
    print("Hello", name)

greet()
# print(name)  ❌ This will cause an error because 'name' is local

Output:
Hello Swathi

2. Global Scope
A variable declared outside any function is global and can be accessed anywhere in the file.

name = "Swathi"  # global variable
def greet():
    print("Hello", name)  # can access global variable

greet()
print("Name outside function:", name)

Output:
Hello Swathi
Name outside function: Swathi

3. Global Keyword
If you want to modify a global variable inside a function, use the global keyword.

name = "Swathi"

def change_name():
    global name
    name = "Anu"

change_name()
print("Name after change:", name)

Output:
Name after change: Anu

4. Nonlocal Keyword (for nested functions)
Used to modify a variable in the outer function scope (not global).

def outer():
    name = "Swathi"

    def inner():
        nonlocal name
        name = "Anu"
        print("Inner:", name)

    inner()
    print("Outer:", name)

outer()

Output:
Inner: Anu  
Outer: Anu
 

 Anonymous Functions

In Python, functions are typically defined using the def keyword. However, Python also allows you to create anonymous functions, which are functions without a name. These are commonly created using the lambda keyword, and are often referred to as lambda functions.

A lambda function is a small, unnamed function defined using the lambda keyword. It is used when you need a simple function for a short period of time — often as an argument to higher-order functions like map(), filter(), and sorted().
  • A lambda function can take any number of arguments
  • It must contain only one expression
  • The result of the expression is automatically returned
Syntax

lambda arguments: expression

Example:

add = lambda x, y: x + y
print(add(3, 5))  

Output: 8

Using Lambda with map()

The map() function applies a function to each item in an iterable (like a list). Lambda functions are perfect for quick transformations using map().

Example:
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x**2, nums))
print(squares)

Output:
[1, 4, 9, 16]

Using Lambda with filter()

The filter() function selects elements from a list based on a condition. 

Example:
nums = [1, 2, 3, 4, 5]
even_nums = list(filter(lambda x: x % 2 == 0, nums))
print(even_nums)

Output:
[2, 4]

Using Lambda with sorted()

You can use lambda to sort a list of tuples based on the second value.

Example:
pairs = [(1, 3), (2, 1), (4, 2)]
sorted_pairs = sorted(pairs, key=lambda x: x[1])
print(sorted_pairs)

Output:
[(2, 1), (4, 2), (1, 3)]

Recursive Functions

A recursive function is a function that calls itself in order to solve a problem. Recursion is a powerful concept that breaks a complex problem into smaller sub-problems of the same type.

Syntax:

def function_name(parameters):
    if base_condition:
        return result
    else:
        return function_name(modified_parameters)

Here,
Base Case: The condition under which the recursion ends.
Recursive Case: The function calls itself with modified arguments to move toward the base case.

Example: 
1. Recursive Function to Calculate Factorial

def factorial(n):
    if n == 0 or n == 1:
        return 1  # base case
    else:
        return n * factorial(n - 1)  # recursive call

# Test the function
num = 5
print("Factorial of", num, "is", factorial(num))

Output:
Factorial of 5 is 120

2. Recursive Function for Fibonacci Series

def fibonacci(n):
    if n == 0:
        return 0  # base case
    elif n == 1:
        return 1  # base case
    else:
        return fibonacci(n - 1) + fibonacci(n - 2)

# Print first 6 Fibonacci numbers
for i in range(6):
    print(fibonacci(i), end=' ')

Output:
0 1 1 2 3 5

OOPS Concept

Python has supported object-oriented programming (OOP) since its inception. It allows developers to build applications using the object-oriented approach, which emphasizes structuring code around objects and classes rather than functions and logic alone.

Key Features of OOP in Python

  • Object-Oriented Language:
    Python is inherently an object-oriented language. This means everything in Python is an object, including functions, classes, and even types.

  • Supports Class and Object Creation:
    Python makes it simple to create and use classes and objects, the core components of object-oriented programming.

  • Object-Oriented Paradigm:
    OOP is a paradigm in which software is designed and structured using classes (blueprints) and objects (instances). This helps in building modular and reusable code.

  • Real-World Mapping:
    Objects in OOP often represent real-world entities such as a book, house, car, or pencil. This makes the code more relatable and easier to manage in large applications.

  • Code Reusability:
    One of the core principles of OOP is to write reusable code. Once a class is defined, it can be reused across different parts of the application, or even in other projects.

  • Problem Solving with Objects:
    OOP is a widely used technique for solving programming problems by creating and interacting with objects that model real or conceptual systems.

Major Principles of Object-Oriented Programming (OOP)

1. Class

A class is a blueprint or template for creating objects. It defines the attributes (variables) and behaviors (methods) that the objects created from the class will have.

 Creating a Class in Python

A class is defined using the class keyword, followed by the class name and a colon.

Syntax:
class ClassName:
    # class body (variables and methods)

Example:
class Car:
    pass  # An empty class


 Instance Variables in Python

An instance variable is unique to each object. It is defined inside the constructor (__init__() method) using the self keyword.

Example:

class Car:
    def __init__(self, brand, color):
        self.brand = brand      # instance variable
        self.color = color      # instance variable

car1 = Car("Toyota", "Red")
car2 = Car("Honda", "Blue")

print(car1.brand)  
print(car2.color)  

Output: 
Toyota
Blue

 Class Variables in Python

A class variable is shared across all instances (objects) of the class. It is defined inside the class but outside any instance method.

Example:

class Car:
    wheels = 4  # class variable

    def __init__(self, brand):
        self.brand = brand  # instance variable

car1 = Car("Toyota")
car2 = Car("Honda")

print(car1.wheels)  # Output: 4
print(car2.wheels)  # Output: 4

Both car1 and car2 share the same wheels class variable.

★ Methods in a Class

method is a function defined inside a class and is used to define the behavior of an object.

2. Object

An object is an instance of a class. It represents a specific entity that has the attributes and behaviors defined by the class.

Syntax:
object_name = ClassName(arguments)
Here,
  • object_name: The name you give to the object.
  • ClassName: The class from which you're creating the object.
  • arguments: The values passed to the class constructor (__init__ method), if required.
Example:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

#  Creating an object  
p1 = Person("Swathi", 24)

#  Calling the method
p1.greet()

 Output:
Hello, my name is Swathi and I am 24 years old.

3. Method

A method is a function that is defined inside a class and is used to perform operations using the object’s data.

1. Instance Methods
 It always takes self as the first parameter, which refers to the instance calling the method.

Syntax:

class MyClass:
    def instance_method(self):
        # access instance variables using self
        pass

Example:

class Car:
    def __init__(self, brand):
        self.brand = brand

    def show_brand(self):  # Instance method
        print("Brand:", self.brand)

car1 = Car("Hyundai")
car1.show_brand()  

Output:
Brand: Hyundai

2. Class Methods
class method is bound to the class, not the instance. It takes cls as the first argument and is used to access or modify class-level data. You define it using the @classmethod decorator.

Syntax:

class MyClass:
    @classmethod
    def class_method(cls):
        # access class variables using cls
        pass

Example:

class Student:
    school = "Navodaya"

    @classmethod
    def show_school(cls):
        print("School Name:", cls.school)

Student.show_school()  

Output: 
School Name: Navodaya

3. Static Methods
static method doesn’t take self or cls as its first argument. It behaves like a regular function but is included inside the class for logical grouping. Use the @staticmethod decorator to define it.

Syntax:

class MyClass:
    @staticmethod
    def static_method():
        # no access to class or instance variables
        pass

Example:

class Math:
    @staticmethod
    def add(x, y):
        return x + y

print(Math.add(3, 5))  

Output: 8

4. Constructor
constructor is a special method that is automatically called when an object of a class is created. It is used to initialize the object’s attributes (variables).
In Python, the constructor method is always named __init__().

Syntax:
class ClassName:
    def __init__(self, parameters):
        # initialization code

Example:

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def display(self):
        print(f"Name: {self.name}, Grade: {self.grade}")

# Creating an object (constructor is automatically called)
s1 = Student("Amrutha", "A")
s1.display()

5. Destructor
destructor is a special method that is called automatically when an object is about to be destroyed (i.e., when it is no longer in use). It is used to perform cleanup activities such as closing files or releasing memory.
In Python, the destructor method is named __del__().

Syntax:
class ClassName:
    def __del__(self):
        # cleanup code

Example:

class Student:
    def __init__(self, name):
        self.name = name
        print(f"Student {self.name} is created.")

    def __del__(self):
        print(f"Student {self.name} is deleted.")

# Object is created
s1 = Student("Amrutha")

# Manually deleting the object (for demonstration)
del s1


4. Inheritance

Inheritance allows a class (called the child or derived class) to inherit attributes and methods from another class (called the parent or base class). It promotes code reusability and builds hierarchical relationships between classes.

Syntax:
class ParentClass:
    # parent members

class ChildClass(ParentClass):
    # child members

The child class inherits all accessible members (attributes and methods) of the parent class.

Example:

# Base class
class Person:
    def __init__(self, name):
        self.name = name

    def display(self):
        print(f"Name: {self.name}")

# Derived class
class Student(Person):
    def __init__(self, name, grade):
        super().__init__(name)  # Call parent constructor
        self.grade = grade

    def show(self):
        print(f"Grade: {self.grade}")

# Creating an object
s1 = Student("Swathi", "A")
s1.display()  # Inherited method
s1.show()     # Child's own method

Output:
Name: Swathi
Grade: A

The super() function is used to call methods from the parent class, especially the constructor (__init__()), ensuring the parent is properly initialized.
super().__init__(name)

★ Single Inheritance
    Single inheritance enables a derived class to inherit properties from a single parent class.


Example:

class Animal:
    def sound(self):
        print("Animal makes sound")

class Dog(Animal):
    def bark(self):
        print("Dog barks")

d = Dog()
d.sound()  # Inherited
d.bark()   # Child's method


★ Multiple Inheritance
    When a class can be derived from more than one base class this type of inheritance is called multiple inheritances.
        

Example:

class Father:
    def skills(self):
        print("Carpentry")

class Mother:
    def skills(self):
        print("Painting")

class Child(Father, Mother):
    def my_skills(self):
        super().skills()  # Depends on method resolution order (MRO)

c = Child()
c.my_skills()  # Output: Carpentry (Father is first in inheritance list)



★ Multilevel Inheritance
    In multilevel inheritance, features of the base class and the derived class are further inherited into the new derived class. 
            

Example:

class Grandparent:
    def family_name(self):
        print("Family name: Sharma")

class Parent(Grandparent):
    def occupation(self):
        print("Doctor")

class Child(Parent):
    def hobby(self):
        print("Painting")

c = Child()
c.family_name()
c.occupation()
c.hobby()



★ Hierarchical Inheritance
    When more than one derived class are created from a single base this type of inheritance is called hierarchical inheritance.
                    

Example:

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

# Child class 1
class Dog(Animal):
    def bark(self):
        print("Dog barks")

# Child class 2
class Cat(Animal):
    def meow(self):
        print("Cat meows")

# Creating objects
d = Dog()
c = Cat()

# Accessing methods
d.speak()
d.bark()

c.speak()
c.meow()

Output:
Animal makes a sound
Dog barks
Animal makes a sound
Cat meows


★ Hybrid Inheritance
    Hybrid inheritance is a combination of more than one type of inheritance. It uses a mix like single, multiple, or multilevel inheritance within the same program.


Example:

# Base class
class Person:
    def details(self):
        print("Person: General details")

# Intermediate class 1
class Student(Person):
    def student_info(self):
        print("Student: Swathi")

# Intermediate class 2
class Employee(Person):
    def employee_info(self):
        print("Employee: Software Developer")

# Child class that combines both
class Intern(Student, Employee):
    def intern_info(self):
        print("Intern: Works part-time and studies")

# Create object
i = Intern()

# Call methods from all classes
i.details()
i.student_info()
i.employee_info()
i.intern_info()

Output:
Person: General details
Student: Swathi
Employee: Software Developer
Intern: Works part-time and studies


5. Polymorphism

Polymorphism means "many forms". It allows the same method to behave differently based on the object that is calling it. This enhances flexibility and interface consistency.

Types of Polymorphism in Python

Python supports two main types of polymorphism:
  1. Duck Typing (Informal / Dynamic Polymorphism)
  2. Method Overriding (Formal / Runtime Polymorphism)
Python does not support method overloading (same method name with different arguments), as seen in some other languages like Java or C++.
Dynamic Polymorphism

Python focuses on behavior rather than the actual type. If an object implements a method, Python doesn't care what class it belongs to.

Example:
class Bird:
    def fly(self):
        print("Bird is flying in the sky.")

class Airplane:
    def fly(self):
        print("Airplane is flying at high altitude.")

class Rocket:
    def fly(self):
        print("Rocket is flying into space.")

# A function that uses duck typing
def start_flying(obj):
    obj.fly()

# Different objects
b = Bird()
a = Airplane()
r = Rocket()

start_flying(b)
start_flying(a)
start_flying(r)

Output:
Bird is flying in the sky.
Airplane is flying at high altitude.
Rocket is flying into space.

Runtime Polymorphism

Method overriding occurs when a child class provides a specific implementation of a method that is already defined in its parent class.

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

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

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

# Objects
a = Animal()
d = Dog()
c = Cat()

# Calling overridden method
a.sound() 
d.sound()  
c.sound()  

Output:
Animal makes a sound.
Dog barks.
Cat meows.

6. Data Abstraction

Abstraction hides the complex implementation details and shows only the essential features of an object. In Python, abstraction can be achieved using abstract classes and interfaces (via the abc module).

Steps to Implement Abstraction in Python

  1. Import ABC and abstractmethod from the abc module.
  2. Create a class that inherits from ABC.
  3. Use the @abstractmethod decorator to define abstract methods.
  4. Create subclasses that implement the abstract methods.
Example:

from abc import ABC, abstractmethod

# Abstract class
class Vehicle(ABC):

    @abstractmethod
    def start_engine(self):
        pass

    @abstractmethod
    def stop_engine(self):
        pass

# Concrete class
class Car(Vehicle):

    def start_engine(self):
        print("Car engine started.")

    def stop_engine(self):
        print("Car engine stopped.")

# Creating object
my_car = Car()
my_car.start_engine()
my_car.stop_engine()

Output:
Car engine started.
Car engine stopped.

7. Encapsulation

Encapsulation is the process of bundling data (variables) and methods that operate on the data into a single unit (class). It also restricts direct access to some of the object’s components, which is useful for data protection.


Python Modules

A module is a collection of related code grouped together in a file that can be reused in other Python programs.

In Python, a module is simply a Python file (.py) that contains:

  • Functions
  • Classes
  • Variables
  • And optionally, executable code
Why Use Modules?
  • Break large code into smaller, organized files
  • Improve code reusability
  • Avoid duplication
  • Makes debugging easier
  • Share code between projects
Create a Module
Create a new .py file (for example, Calci.py) and define your functions:

Example:
# Calci.py

def add(a, b):
    return a + b

def sub(a, b):
    return a - b

def mul(a, b):
    return a * b

def div(a, b):
    return a / b

Importing a Module

To use the functions defined in Calci.py, import it into another Python script using the import keyword.
You can use any function from the module using the format ModuleName.function().

Example:
# main.py

import Calci

print(Calci.add(10, 5))  # Output: 15
print(Calci.sub(10, 5))  # Output: 5
print(Calci.mul(10, 5))  # Output: 50
print(Calci.div(10, 5))  # Output: 2.0

Importing specific functions from Module

You don’t have to import the entire module — you can import only the functions you need.

Example:

from Calci import add, mul

print(add(10, 5))     # Output: 15
print(mul(10, 5))     # Output: 50
# print(sub(10, 5))   ❌ Error: Not imported

★ Math Module

The math module in Python provides a wide range of mathematical functions and constants, making it easier to perform complex mathematical computations.

It includes:

  • Trigonometric functions
  • Power and logarithmic functions
  • Angle conversions
  • Rounding functions
  • Mathematical constants like π (pi) and e
To use these functions, you must first import the math module:
import math

Math Module Constants

Constant Description Example Output
math.pi The value of π (pi) ≈ 3.14159 math.pi 3.141592653589793
math.e The value of e ≈ 2.71828 (Euler's number) math.e 2.718281828459045
math.tau τ (tau) = 2π math.tau 6.283185307179586
math.inf Positive infinity math.inf inf
math.nan Not a Number (NaN) math.nan nan
 
Math Module Functions

Function Description Example Output
math.sqrt(x) Square root of x math.sqrt(16) 4.0
math.pow(x, y) x raised to the power y math.pow(2, 3) 8.0
math.floor(x) Largest integer ≤ x math.floor(4.7) 4
math.ceil(x) Smallest integer ≥ x math.ceil(4.2) 5
math.factorial(x) Factorial of x math.factorial(5) 120
math.fabs(x) Absolute value (float) math.fabs(-3.7) 3.7
math.log(x, base) Logarithm of x to given base math.log(100, 10) 2.0
math.exp(x) Returns eˣ math.exp(1) 2.71828...
math.sin(x) Sine of x (x in radians) math.sin(math.pi/2) 1.0
math.cos(x) Cosine of x (x in radians) math.cos(0) 1.0
math.radians(x) Converts degrees to radians math.radians(180) 3.14159...
math.degrees(x) Converts radians to degrees math.degrees(math.pi) 180.0

Example:

import math

# Constants
print("Pi:", math.pi)
print("Euler's number (e):", math.e)
print("Tau (2π):", math.tau)

# Functions
print("Square root of 16:", math.sqrt(16))
print("2 to the power 3:", math.pow(2, 3))
print("Floor of 4.7:", math.floor(4.7))
print("Ceil of 4.2:", math.ceil(4.2))
print("Factorial of 5:", math.factorial(5))
print("Absolute value of -3.7:", math.fabs(-3.7))
print("Log base 10 of 100:", math.log(100, 10))
print("Exponential of 1:", math.exp(1))

# Trigonometry
print("Sine of π/2 radians:", math.sin(math.pi/2))
print("Cosine of 0 radians:", math.cos(0))

# Angle conversions
print("180 degrees in radians:", math.radians(180))
print("π radians in degrees:", math.degrees(math.pi))

Output:

Pi: 3.141592653589793
Euler's number (e): 2.718281828459045
Tau (2π): 6.283185307179586
Square root of 16: 4.0
2 to the power 3: 8.0
Floor of 4.7: 4
Ceil of 4.2: 5
Factorial of 5: 120
Absolute value of -3.7: 3.7
Log base 10 of 100: 2.0
Exponential of 1: 2.718281828459045
Sine of π/2 radians: 1.0
Cosine of 0 radians: 1.0
180 degrees in radians: 3.141592653589793
π radians in degrees: 180.0


Datetime Module

The datetime module in Python provides classes and functions for working with dates, times, and date-time combinations.
It is widely used in:
  • Logging and timestamping
  • Scheduling events
  • Calculating date differences
  • Formatting dates and times
To use it, you must import the module:
                import datetime

Classes in the datetime Module

Class Description
date   Handles calendar date (year, month, day)
time   Handles time (hour, minute, second, microsecond)
datetime   Combination of date and time
timedelta   Represents difference between two date/time
timezone   Handles time zones

date Class – Working with Dates

from datetime import date

today = date.today() # get today's date
print("Today's Date:", today)

print("Year:", today.year)
print("Month:", today.month)
print("Day:", today.day)

Output:
Today's Date: 2025-08-17
Year: 2025
Month: 8
Day: 17

time Class- Working with Time

from datetime import time

t = time(14, 30, 45)  # 2:30:45 PM
print("Time:", t)
print("Hour:", t.hour)
print("Minute:", t.minute)
print("Second:", t.second)

Output:
Time: 14:30:45
Hour: 14
Minute: 30
Second: 45

datetime Class – Working with Date & Time Together

from datetime import datetime

now = datetime.now() # get current date & time
print("Current Date & Time:", now)

Output:
Current Date & Time: 2025-08-17 12:34:56.789123

timedelta – Date/Time Arithmetic

from datetime import timedelta

future = today + timedelta(days=10)
print("10 days from now:", future)

past = today - timedelta(days=5)
print("5 days ago:", past)

Output:
10 days from now: 2025-08-26
5 days ago: 2025-08-11

Formatting Dates and Times
Python provides strftime() to format date/time objects into readable strings.

Common Format Codes

Code Meaning Example
%Y Full year (4 digits)  2025
%m Month (01–12)  08
%d Day of the month  17
%H Hour (24-hour)  14
%M Minute  30
%S Second  45

Example:
formatted = now.strftime("%Y-%m-%d %H:%M:%S")
print("Formatted Date-Time:", formatted)

Output:
Formatted Date-Time: 2025-08-17 12:34:56

Packages

A package in Python is a way of organizing related Python modules (i.e., .py files). It's essentially a directory that:
  • Contains an __init__.py file (can be empty or contain initialization code).
  • May include multiple Python module files.
  • Allows for better code organization, modularity, and reuse.
Structure of a Package

Here is an example package:

calculator/
├── __init__.py
├── add.py
└── subtract.py

Module Files

add.py

def add(a, b):
    return a + b

subtract.py

def subtract(a, b):
    return a - b

__init__.py

from .add import add
from .subtract import subtract

The __init__.py file makes the folder a package and allows importing specific functions directly from the package.

To use the calculator package in a script:

from calculator import add, subtract

print(add(5, 3))       
print(subtract(5, 3))   

Output: 
8
2

Exception Handling

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. It usually happens when the program encounters an error condition (e.g., trying to divide by zero, accessing a missing file, etc.).
  • Exceptions do not crash the program immediately if they are properly caught and handled.
  • They are objects in Python, derived from the built-in BaseException class.
  • Python provides a powerful way to detect, raise, and handle exceptions using try, except, else, and finally blocks.

Exception Flow in Python

  1. Error occurs → Python raises an exception.
  2. Try block looks for matching except clause.
  3. If matched, the exception is caught and handled.
  4. If not matched, the program terminates and shows a traceback.
 
Types of Exceptions in Python

Python exceptions are broadly categorized into:

1. Built-in Exceptions

These are predefined exceptions that are part of the Python standard library. They're raised during various runtime errors and are grouped by their causes:

Arithmetic Errors
Raised during math-related operations:
  • ZeroDivisionError: Division or modulo by zero.
  • OverflowError: Numeric calculation exceeds limits.
Lookup Errors
Errors when accessing invalid indices or keys:
  • IndexError: Index out of range in a sequence (like a list or tuple).
  • KeyError: Non-existent key used in a dictionary.
Type and Value Errors
  • TypeError: Operation or function applied to an inappropriate type.    Example: '2' + 3 
  • ValueError: Function gets an argument of the right type but an invalid value. Example: int("abc")
File and I/O Errors
Errors related to file handling and input/output:
  • FileNotFoundError: Trying to open a file that doesn't exist.
  • PermissionError: Insufficient permissions to access a file.
Import Errors
Errors while importing modules:
  • ImportError: General import failure.
  • ModuleNotFoundError: Specific module could not be found.
Assertion Errors
  • AssertionError: Raised when an assert statement fails. Example: assert 2 + 2 == 5

2. User-Defined Exceptions 

Python allows you to define custom exceptions to handle application-specific errors by subclassing the built-in Exception class.

Example: Custom Exception for Negative Values

class NegativeValueError(Exception):
    """Custom exception for negative values."""
    pass

def check_positive(value):
    if value < 0:
        raise NegativeValueError("Negative values are not allowed.")
    return value

try:
    check_positive(-10)
except NegativeValueError as e:
    print(e)

Output:
Negative values are not allowed.




















Comments

Popular posts from this blog

Getting started with Python, Strings

Numpy and Data Handling using Pandas