UNIT -2
File Handling in Python
File handling refers to the process of performing operations on a file, such as creating,
opening, reading, writing and closing it through a programming interface. It involves
managing the data flow between the program and the file system on the storage device,
ensuring that data is handled safely and efficiently.
Why do we need File Handling
• To store data permanently, even after the program ends.
• To access external files like .txt, .csv, .json, etc.
• To process large files efficiently without using much memory.
• To automate tasks like reading configs or saving outputs.
• To handle input/output in real-world applications and tools.
File Objects in Python
• A file object allows us to use, access and manipulate all the user accessible files. One
can read and write any such files. When a file operation fails for an I/O-related
reason, the exception IOError is raised. This includes situations where the operation is
not defined for some reason, like seek() on a tty device or writing a file opened for
reading.
Key aspects of file objects in Python:
• Creation: The canonical way to create a file object is by using the built-
in open() function. This function takes the file path and an access mode (e.g., 'r' for
read, 'w' for write, 'a' for append) as arguments and returns a file object.
Python
file_object = open("example.txt", "r") # Opens "example.txt" in read mode
Diiferent modes:
• open(): Opens a file in given access mode.
• open(file_address, access_mode)
• Examples of accessing a file: A file can be opened with a built-in function called
open(). This function takes in the file’s address and the access_mode and returns
a file object. There are different types of access_modes:
• r: Opens a file for reading only
• r+: Opens a file for both reading and writing
• w: Opens a file for writing only
• w+: Open a file for writing and reading.
• a: Opens a file for appending
• a+: Opens a file for both appending and reading
When you add 'b' to the access modes you can read the file in binary format rather than the
default text format. It is used when the file to be accessed is not in text.
In Python, a file object (also known as a file-like object or stream) is an abstract
representation that allows interaction with a file or other I/O resource. It provides
methods and attributes to perform operations such as reading from, writing to, and
manipulating the underlying data.
Context Manager (with statement):
It is highly recommended to use file objects with the with statement. This ensures that the file
is automatically closed, even if errors occur, preventing resource leaks.
Python
with open("example.txt", "w") as f:
f.write("Hello, world!")
# File is automatically closed here
Methods and Attributes:
File objects expose various methods and attributes for interacting with the file content:
• read(): Reads the entire content or a specified number of
bytes/characters.
• readline(): Reads a single line.
• readlines(): Reads all lines into a list.
• write(): Writes a string or bytes to the file.
• writelines(): Writes a list of strings to the file.
• close(): Closes the file, releasing system resources.
• seek(): Changes the current file position.
• tell(): Returns the current file position.
• encoding: Attribute for text files, indicating the encoding used.
read([size]): It reads the entire file and returns it contents in the form of a string. Reads at
most size bytes from the file (less if the read hits EOF before obtaining size bytes). If the size
argument is negative or omitted, read all data until EOF is reached.
# Reading a file
f = open(__file__, 'r')
#read()
text = f.read(10)
print(text)
f.close()
readline([size]): It reads the first line of the file i.e till a newline character or an EOF in case
of a file having a single line and returns a string. If the size argument is present and non-
negative, it is a maximum byte count (including the trailing newline) and an incomplete line
may be returned. An empty string is returned only when EOF is encountered immediately.
# Reading a line in a file
f = open(__file__, 'r')
#readline()
text = f.readline(20)
print(text)
f.close()
readlines([sizehint]): It reads the entire file line by line and updates each line to a list which
is returned.Read until EOF using readline() and return a list containing the lines thus read. If
the optional sizehint argument is present, instead of reading up to EOF, whole lines totalling
approximately sizehint bytes (possibly after rounding up to an internal buffer size) are read.
# Reading a file
f = open(__file__, 'r')
#readline()
text = f.readlines(25)
print(text)
f.close()
write(string): It writes the contents of string to the file. It has no return value. Due to
buffering, the string may not actually show up in the file until the flush() or close() method is
called.
# Writing a file
f = open(__file__, 'w')
line = 'Welcome Geeks\n'
#write()
f.write(line)
f.close()
More Examples in different modes:
# Reading and Writing a file
f = open(__file__, 'r+')
lines = f.read()
f.write(lines)
f.close()
# Writing and Reading a file
f = open(__file__, 'w+')
lines = f.read()
f.write(lines)
f.close()
# Appending a file
f = open(__file__, 'a')
lines = 'Welcome Geeks\n'
f.write(lines)
f.close()
# Appending and reading a file
f = open(__file__, 'a+')
lines = f.read()
f.write(lines)
f.close()
writelines(sequence): It is a sequence of strings to the file usually a list of strings or any
other iterable data type. It has no return value.
# Writing a file
f = open(__file__, 'a+')
lines = f.readlines()
#writelines()
f.writelines(lines)
f.close()
• tell(): It returns an integer that tells us the file object’s position from the beginning of
the file in the form of bytes
# Telling the file object position
f = open(__file__, 'r')
lines = f.read(10)
#tell()
print(f.tell())
f.close()
seek(offset, from_where): It is used to change the file object’s position. Offset indicates the
number of bytes to be moved. from_where indicates from where the bytes are to be moved.
# Setting the file object position
f = open(__file__, 'r')
lines = f.read(10)
print(lines)
#seek()
print(f.seek(2,2))
lines = f.read(10)
print(lines)
f.close()
flush(): Flush the internal buffer, like stdio‘s fflush(). It has no return value. close()
automatically flushes the data but if you want to flush the data before closing the file then
you can use this method.
# Clearing the internal buffer before closing the file
f = open(__file__, 'r')
lines = f.read(10)
#flush()
f.flush()
print(f.read())
f.close()
fileno(): Returns the integer file descriptor that is used by the underlying implementation to
request I/O operations from the operating system.
# Getting the integer file descriptor
f = open(__file__, 'r')
#fileno()
print(f.fileno())
f.close()
• isatty(): Returns True if the file is connected to a tty(-like) device and False if
not.
# Checks if file is connected to a tty(-like) device
f = open(__file__, 'r')
#isatty()
print(f.isatty())
f.close()
next(): It is used when a file is used as an iterator. The method is called repeatedly. This
method returns the next input line or raises StopIteration at EOF when the file is open for
reading( behaviour is undefined when opened for writing).
# Iterates over the file
f = open(__file__, 'r')
#next()
try:
while f.next():
print(f.next())
except:
f.close()
truncate([size]): Truncate the file's size. If the optional size argument is present, the file is
truncated to (at most) that size. The size defaults to the current position. The current file
position is not changed. Note that if a specified size exceeds the file's current size, the result
is platform-dependent: possibilities include that the file may remain unchanged, increase to
the specified size as if zero-filled, or increase to the specified size with undefined new
content.
# Truncates the file
f = open(__file__, 'w')
#truncate()
f.truncate(10)
f.close()
close(): Used to close an open file. A closed file cannot be read or written any more.
# Opening and closing a file
f = open(__file__, 'r')
#close()
f.close()
Attributes:
closed: returns a boolean indicating the current state of the file object. It returns true if the
file is closed and false when the file is open.
encoding: The encoding that this file uses. When Unicode strings are written to a file, they
will be converted to byte strings using this encoding.
mode: The I/O mode for the file. If the file was created using the open() built-in function,
this will be the value of the mode parameter.
name: If the file object was created using open(), the name of the file.
newlines: A file object that has been opened in universal newline mode have this attribute
which reflects the newline convention used in the file. The value for this attribute are "\r",
"\n", "\r\n", None or a tuple containing all the newline types seen.
softspace: It is a boolean that indicates whether a space character needs to be printed before
another value when using the print statement.
f = open(__file__, 'a+')
print(f.closed)
print(f.encoding)
print(f.mode)
print(f.newlines)
print(f.softspace)
Types:
File objects can be categorized into:
• Raw binary files: Handle raw bytes.
• Buffered binary files: Provide buffering for efficiency when handling binary
data.
• Text files: Handle text data, often with encoding and decoding applied.
Standard Files in Python
In Python, standard files usually refer to the three standard streams that every program has by
default. These are available through the built-in sys module:
1. Standard Input (stdin)
o File object: sys.stdin
o Used for input (by default, the keyboard).
o Example:
o import sys
o data = sys.stdin.readline() # Reads a line from user input
o print("You entered:", data)
2. Standard Output (stdout)
o File object: sys.stdout
o Used for normal program output (by default, the console).
o Example:
o import sys
sys.stdout.write("Hello, World!\n") # Similar to print()
o
3. Standard Error (stderr)
o File object: sys.stderr
o Used for error messages (separate from normal output).
o Example:
o import sys
o sys.stderr.write("This is an error message!\n")
Notes:
• All three (stdin, stdout, stderr) are file-like objects.
• They support common file operations such as .read(), .write(), and .flush().
• You can redirect these streams to files or other outputs.
Example of redirection:
import sys
# Redirect stdout to a file
with open("output.txt", "w") as f:
sys.stdout = f
print("This goes into the file instead of console.")
# Redirect stderr to a file
with open("error.log", "w") as f:
sys.stderr = f
raise Exception("This error will be written into error.log")
sys.stderr is redirected to the file error.log.
The line raise Exception("...") raises an exception.
Normally, Python prints the error traceback to stderr.
Since stderr now points to error.log, the traceback goes into the file instead of the console.
So the error still occurs (because we explicitly raised it), but you won’t see it in the console
,it gets written into error.log.
Command Line Arguments in Python
The arguments that are given after the name of the program in the command line shell of the
operating system are known as Command Line Arguments. Python provides various ways
of dealing with these types of arguments. The three most common are:
• Using sys.argv
• Using getopt module
• Using argparse module
Using sys.argv
The sys module provides functions and variables used to manipulate different parts of the
Python runtime environment. This module provides access to some variables used or
maintained by the interpreter and to functions that interact strongly with the interpreter.
One such variable is sys.argv which is a simple list structure. It's main purpose are:
• It is a list of command line arguments.
• len(sys.argv) provides the number of command line arguments.
• sys.argv[0] is the name of the current Python script.
Example: Let's suppose there is a Python script for adding two numbers and the numbers are
passed as command-line arguments.
# Python program to demonstrate
# command line arguments
import sys
# total arguments
n = len(sys.argv)
print("Total arguments passed:", n)
# Arguments passed
print("\nName of Python script:", sys.argv[0])
print("\nArguments passed:", end = " ")
for i in range(1, n):
print(sys.argv[i], end = " ")
# Addition of numbers
Sum = 0
# Using argparse module
for i in range(1, n):
Sum += int(sys.argv[i])
print("\n\nResult:", Sum)
Output:
Using getopt module
Python getopt module is similar to the getopt() function of C. Unlike sys module getopt
module extends the separation of the input string by parameter validation. It allows both short
and long options including a value assignment. However, this module requires the use of the
sys module to process input data properly. To use getopt module, it is required to remove the
first element from the list of command-line arguments.
Syntax: getopt.getopt(args, options, [long_options])
Parameters:
• args: List of arguments to be passed.
• options: String of option letters that the script want to recognize. Options that require
an argument should be followed by a colon (:).
• long_options: List of string with the name of long options. Options that require
arguments should be followed by an equal sign (=).
• Return Type: Returns value consisting of two elements: the first is a list of (option,
value) pairs. The second is the list of program arguments left after the option list was
stripped.
• Example:
• import getopt, sys
•
•
• # Remove 1st argument from the
• # list of command line arguments
• argumentList = sys.argv[1:]
•
• # Options
• options = "hmo:"
•
• # Long options
• long_options = ["Help", "My_file", "Output="]
•
• try:
• # Parsing argument
• arguments, values = getopt.getopt(argumentList, options, long_options)
•
• # checking each argument
• for currentArgument, currentValue in arguments:
•
• if currentArgument in ("-h", "--Help"):
• print ("Displaying Help")
•
• elif currentArgument in ("-m", "--My_file"):
• print ("Displaying file_name:", sys.argv[0])
•
• elif currentArgument in ("-o", "--Output"):
• print (("Enabling special output mode (% s)") % (currentValue))
•
• except getopt.error as err:
• # output error, and return with an error code
• print (str(err))
Output:
Using argparse module
Using argparse module is a better option than the above two options as it provides a lot of
options such as positional arguments, default value for arguments, help message, specifying
data type of argument etc.
Note: As a default optional argument, it includes -h, along with its long version --help.
Example 1: Basic use of argparse module.
import argparse
# Initialize parser
parser = argparse.ArgumentParser()
parser.parse_args()
Output:
Example 2: Adding description to the help message.
# Python program to demonstrate
# command line arguments
import argparse
msg = "Adding description"
# Initialize parser
parser = argparse.ArgumentParser(description = msg)
parser.parse_args()
Output:
File System in Python
In python, the file system contains the files and directories. To handle these files and
directories python supports “os” module. Python has the “os” module, which provides us
with many useful methods to work with directories (and files as well).
The os module provides us the methods that are involved in file processing operations and
directory processing like renaming, deleting, get current directory, changing directory etc.
Renaming the file - rename()
The os module provides us the rename() method which is used to rename the specified file to
a new name.
Syntax:
os.rename (“current-name”, “new-name”)
Example:
import os;
#rename file2.txt to file3.txt
os.rename("file2.txt","file3.txt")
Removing the file – remove()
The os module provides us the remove() method which is used to remove the specified
file.
Syntax:
os.remove(“file-name”)
Example:
import os;
#deleting the file named file3.txt
os.remove("file3.txt")
Creating the new directory – mkdir()
The mkdir() method is used to create the directories in the current working directory.
Syntax:
os.mkdir(“directory-name”)
Example:
import os;
#creating a new directory with the name new
os.mkdir("dirnew")
Changing the current working directory – chdir()
The chdir() method is used to change the current working directory to a specified directory.
Syntax:
os.chdir("new-directory")
Example:
import os;
#changing the current working directory to new
os.chdir("dir2")
Get current working directory – getpwd()
This method returns the current working directory.
Syntax:
os.getcwd()
Example:
import os;
#printing the current working directory
print(os.getcwd())
Deleting directory - rmdir()
The rmdir() method is used to delete the specified directory.
Syntax:
os.rmdir(“directory name”)
Example:
import os;
#removing the new directory
os.rmdir("dir2")
List Directories and Files – listdir()
All files and sub directories inside a directory can be known using the listdir() method. This
method takes in a path and returns a list of sub directories and files in that path. If no path is
specified, it returns from the current working directory.
Syntax:
os.listdir([“path”])
Example:
import os;
#list of files and directories in current working directory
print(os.listdir())
#list of files and directories in specified path
print(os.listdir(“D:\\”))
File Execution:
File execution in Python refers to the process of running a Python script or code. There are
several common methods for achieving this:
1. Command Line Execution:
• Running a .py file: Navigate to the directory containing your Python script in a
terminal or command prompt and execute it using the Python interpreter:
python your_script_name.py
(On Linux/macOS, you might use python3 to explicitly use Python 3).
• Running a module: Use the -m option to execute a Python module:
python -m module_name
2. Interactive Mode:
• Directly in the interpreter:
Open a terminal or command prompt, type python (or python3), and press Enter to enter the
interactive Python shell. You can then type and execute Python code line by line.
• Using exec():
Within the interactive interpreter, you can execute a file using the exec() function:
Python
exec(open("your_script_name.py").read())
3. Integrated Development Environments (IDEs):
• Most Python-friendly IDEs like PyCharm, VS Code, or Spyder provide integrated
functionalities to run Python files. Typically, you can open your .py file and use a
"Run" button, menu option (e.g., "Run Module"), or keyboard shortcut (e.g., F5) to
execute the script within the IDE's environment.
4. Executing one Python file from another:
• import statement: Import another Python file as a module to access its functions,
classes, and variables. This executes the imported file's code upon import.
Python
import another_script
• subprocess module: Run another Python script as a separate process using
the subprocess module, allowing for more control over execution and input/output.
Python
import subprocess
subprocess.run(["python", "another_script.py"])
• os.system(): Execute a shell command to run another Python file (less recommended
due to security and error handling limitations compared to subprocess).
Python
import os
os.system("python another_script.py")
Presistent storage modules ,related modules
When we talk about persistent storage modules, we usually mean modules that allow data to
be stored and retrieved across multiple program runs (so data doesn’t vanish when the
program ends).
Persistent storage modules in Python (i.e., modules that allow data to be stored permanently
on disk instead of vanishing when the program ends).
Some common persistent data modules in Python are:
Module Purpose Example Use
Serializes Python objects into a binary format and Saving ML models,
pickle
restores them later. caching data.
Stores Python objects in a dictionary-like database Key-value persistent
shelve
file. storage.
Lightweight databases for
dbm Provides simple key-value databases.
configs.
Storing structured data
sqlite3 Built-in lightweight relational database.
with SQL.
Config files, data
json Stores data in human-readable JSON format.
exchange.
1.Using pickle
import pickle
data = {"name": "Alice", "age": 25}
# Save
with open("data.pkl", "wb") as f:
pickle.dump(data, f)
# Load
with open("data.pkl", "rb") as f:
loaded = pickle.load(f)
print(loaded)
2. Using shelve
import shelve
with shelve.open("mydata") as db:
db["user"] = {"name": "Bob", "age": 30}
with shelve.open("mydata") as db:
print(db["user"])
3. Using sqlite3
import sqlite3
conn = sqlite3.connect("mydb.sqlite")
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY,
name TEXT)")
cur.execute("INSERT INTO users (name) VALUES (?)", ("Charlie",))
conn.commit()
cur.execute("SELECT * FROM users")
print(cur.fetchall())
conn.close()
In short:
• For objects → pickle / shelve.
• For structured data → sqlite3 / csv.
• For config / text-based storage → json / configparser.
Python Exception Handling
Python Exception Handling handles errors that occur during the execution of a program.
Exception handling allows to respond to the error, instead of crashing the running program.
It enables you to catch and manage errors, making your code more robust and user-friendly.
Let's look at an example:
Python Exception Handling handles errors that occur during the execution of a program.
Exception handling allows to respond to the error, instead of crashing the running program. It
enables you to catch and manage errors, making your code more robust and user-friendly.
Let's look at an example:
# Simple Exception Handling Example
n = 10
try:
res = n / 0 # This will raise a ZeroDivisionError
except ZeroDivisionError:
print("Can't be divided by zero!")
Output
Can't be divided by zero!
Explanation: In this example, dividing number by 0 raises a ZeroDivisionError. The try
block contains the code that might cause an exception and the except block handles the
exception, printing an error message instead of stopping the program.
Difference Between Exception and Error
• Error: Errors are serious issues that a program should not try to handle. They are
usually problems in the code's logic or configuration and need to be fixed by the
programmer. Examples include syntax errors and memory errors.
• Exception: Exceptions are less severe than errors and can be handled by the program.
They occur due to situations like invalid input, missing files or network issues.
# Syntax Error (Error)
print("Hello world" # Missing closing parenthesis
# ZeroDivisionError (Exception)
n = 10
res = n / 0
Explanation: A syntax error is a coding mistake that prevents the code from running. In
contrast, an exception like ZeroDivisionError can be managed during the program's execution
using exception handling.
Syntax and Usage
Exception handling in Python is done using the try, except, else and finally blocks.
try:
# Code that might raise an exception
except SomeException:
# Code to handle the exception
else:
# Code to run if no exception occurs
finally:
# Code to run regardless of whether an exception occurs
try, except, else and finally Blocks
• try Block: try block lets us test a block of code for errors. Python will "try" to execute
the code in this block. If an exception occurs, execution will immediately jump to the
except block.
• except Block: except block enables us to handle the error or exception. If the code
inside the try block throws an error, Python jumps to the except block and executes it.
We can handle specific exceptions or use a general except to catch all exceptions.
• else Block: else block is optional and if included, must follow all except blocks. The
else block runs only if no exceptions are raised in the try block. This is useful for code
that should execute if the try block succeeds.
• finally Block: finally block always runs, regardless of whether an exception occurred
or not. It is typically used for cleanup operations (closing files, releasing resources).
Example:
try:
n=0
res = 100 / n
except ZeroDivisionError:
print("You can't divide by zero!")
except ValueError:
print("Enter a valid number!")
else:
print("Result is", res)
finally:
print("Execution complete.")
Output
You can't divide by zero!
Execution complete.
Explanation:
• try block asks for user input and tries to divide 100 by the input number.
• except blocks handle ZeroDivisionError and ValueError.
• else block runs if no exception occurs, displaying the result.
• finally block runs regardless of the outcome, indicating the completion of execution.
Exception in Python
In Python, an exception is an error that occurs during the execution of a program.
Instead of crashing immediately, Python gives you a chance to handle the error using try-
except.
Types of Exceptions in Python
Exceptions in Python can broadly be divided into two categories:
1. Built-in Exceptions: These are pre-defined exceptions that come with Python. They
are available in the exceptions module (built-in).
2. 2. User-defined (Custom) Exceptions: You can create your own exceptions by
inheriting from the built-in Exception class.
Built-in Exceptions
Examples:
• Arithmetic Errors
o ZeroDivisionError → divide by zero
o OverflowError → number too large
o FloatingPointError → floating-point operation failed
• Lookup Errors
o IndexError → index out of range
o KeyError → dictionary key not found
• Type & Value Errors
o TypeError → wrong data type used
o ValueError → wrong value given
• File & I/O Errors
o FileNotFoundError → file not found
o IOError → input/output error
• Import & Attribute Errors
o ImportError / ModuleNotFoundError → module not found
o AttributeError → invalid attribute/method
• Others
o MemoryError → memory overflow
o StopIteration → end of iteration
o AssertionError → assertion fails
Example: Built-in Exception
try:
num = int("abc") # invalid integer conversion
except ValueError as e:
print("Error caught:", e)
Output:
Error caught: invalid literal for int() with base 10: 'abc'
User-defined (Custom) Exceptions
Useful when you want to enforce rules specific to your program.
Example: Custom Exception
# Define a custom exception
class NegativeAgeError(Exception):
pass
# Use it
age = -5
try:
if age < 0:
raise NegativeAgeError("Age cannot be negative!")
except NegativeAgeError as e:
print("Custom Error:", e)
Output:
Custom Error: Age cannot be negative!
Exception When it Occurs Example Code Output/Error
ZeroDivisionError:
ZeroDivisionError Division by zero print(10 / 0)
division by zero
Invalid type for ValueError: invalid literal
ValueError int("abc")
conversion for int()
Wrong data types TypeError: can only
TypeError print("5" + 5)
used together concatenate str (not "int")
Accessing invalid lst = [1,2,3]; IndexError: list index out
IndexError
list index print(lst[5]) of range
Key not found in d = {"a":1};
KeyError KeyError: 'b'
dictionary print(d["b"])
FileNotFoundError: [Errno
FileNotFoundError File doesn’t exist open("nofile.txt")
2] No such file
Invalid AttributeError: 'str' object
AttributeError "hello".append("!")
method/attribute has no attribute 'append'
ModuleNotFoundError: No
Importing non-
ImportError import notamodule module named
existing module
'notamodule'
Exception When it Occurs Example Code Output/Error
import math; OverflowError: math range
OverflowError Number too large
print(math.exp(1000)) error
Here’s a classification chart (tree diagram) of Python exceptions 📊:
• Two main types → Built-in Exceptions & User-defined Exceptions
• Built-in are further grouped (Arithmetic, Lookup, Type/Value, I/O, Import/Attribute,
Others)
• User-defined → custom exceptions created by inheriting from Exception class.
Detecting and handling exception
1. Detecting Exceptions
• In Python, exceptions occur during runtime (not at compile time).
• If an error occurs and is not handled, the program will stop execution and display a
traceback.
👉 Example (no handling):
print("Start")
num = int("abc") # invalid conversion
print("End") # ❌ This line will never run
Output:
ValueError: invalid literal for int() with base 10: 'abc'
Here, Python detected a ValueError and stopped execution.
2. Handling Exceptions (try-except)
We use try-except blocks to catch (handle) exceptions so the program continues.
👉 Example:
print("Start")
try:
num = int("abc") # may cause error
except ValueError:
print("❌ Invalid number entered")
print("End")
Output:
Start
❌ Invalid number entered
End
3. Handling Multiple Exceptions
You can handle specific exceptions separately.
try:
num1 = int(input("Enter a number: "))
num2 = int(input("Enter another number: "))
result = num1 / num2
print("Result:", result)
except ZeroDivisionError:
print("❌ Cannot divide by zero")
except ValueError:
print("❌ Only numbers are allowed")
4. Catching Any Exception (Generic Handling)
try:
x = 10 / 0
except Exception as e:
print("Caught an error:", e)
5. Using else with try-except
• else runs only if no exception occurs.
try:
x=5/1
except ZeroDivisionError:
print("Error: division by zero")
else:
print("Division successful:", x)
6. Using finally
• finally block always executes, whether an exception occurs or not.
try:
file = open("data.txt", "r")
except FileNotFoundError:
print("❌ File not found")
finally:
print("✅ Finished execution (cleanup here)")
Summary:
• Detecting → Python finds runtime errors (exceptions).
• Handling → Use try-except-else-finally to manage them.
Here’s the flowchart of exception handling in Python ⚡:
• Code enters the try block
• If an exception occurs → goes to except block
• If no exception → goes to else block
• After that, the finally block always executes
• Then program ends
Context Management
1. What is Context Management?
• Context management is a way to manage resources (like files, database
connections, network sockets) in Python.
• The with statement is used as a context manager.
• It ensures that resources are properly cleaned up (closed/released) even if an
exception occurs.
2. Without Context Manager ❌
If you open a file without context management, you must remember to close it:
try:
file = open("example.txt", "r")
data = file.read()
print(data)
except FileNotFoundError:
print("❌ File not found")
finally:
file.close() # must close manually
If an exception occurs before file.close(), resources may stay open.
3. With Context Manager ✅
Using with statement, Python automatically handles cleanup:
try:
with open("example.txt", "r") as file:
data = file.read()
print(data)
except FileNotFoundError:
print("❌ File not found")
👉 Here:
• with open(...) as file: → creates a context manager
• After the block, file is closed automatically, even if an exception happens
4. Custom Context Manager
You can create your own context manager using __enter__ and __exit__ methods.
class MyResource:
def __enter__(self):
print("🔹 Resource acquired")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("🔸 Resource released (even if error occurred)")
return False # re-raise exception if any
# Using custom context manager
try:
with MyResource() as r:
print("Working with resource")
1 / 0 # causes ZeroDivisionError
except ZeroDivisionError:
print("❌ Division by zero handled")
Output:
🔹 Resource acquired
Working with resource
🔸 Resource released (even if error occurred)
❌ Division by zero handled
5. Why Context Management is Important in Exception Handling?
• Ensures safe cleanup (files closed, DB connections released).
• Prevents resource leaks.
• Makes code shorter & cleaner.
• Works seamlessly with exception handling (try-except-finally).
✅ Summary:
• Use with for automatic resource management.
• Built-in objects (like open) already support it.
• You can also create custom context managers.
Here’s the flow diagram of Context Management (with statement) in Python ⚡:
• __enter__() → acquires resource
• Code block runs
• If exception occurs → details passed to __exit__()
• If no exception → still calls __exit__()
• Resource is always released safely
Comparison: try-finally vs with
Feature try-finally with (Context Manager)
Syntax More verbose Short and clean
Resource
Must be written manually in finally Automatic via __exit__()
Cleanup
Readability Less readable, boilerplate code More readable, concise
Guarantees cleanup (even on
Error Safety Risk of forgetting close() or cleanup
error)
Feature try-finally with (Context Manager)
File handling, DB connections,
Use Cases When no context manager exists
sockets, custom resources
python\ntry:\n f = open("file.txt")\n data python\nwith open("file.txt") as
Example
= f.read()\nfinally:\n f.close()\n f:\n data = f.read()\n
Summary:
• Both ensure cleanup even after exceptions.
• with is the modern, Pythonic way → cleaner, safer, less code.
• Use try-finally only when a context manager is not available.
Example: File Handling
Using try-finally
try:
file = open("example.txt", "r") # open file
data = file.read()
print("File contents:", data)
except FileNotFoundError:
print("❌ File not found")
finally:
file.close() # must remember to close
• Problem: You must explicitly write file.close() in the finally block.
• If you forget, the file may stay open → resource leak.
Using with (Context Manager)
try:
with open("example.txt", "r") as file: # automatically closes
data = file.read()
print("File contents:", data)
except FileNotFoundError:
print("❌ File not found")
• Advantage: No need to write file.close().
• File is closed automatically when the with block ends, even if an exception occurs.
Output (for both, if file missing):
❌ File not found
✅ Conclusion:
• try-finally → works, but longer & error-prone.
• with → cleaner, safer, Pythonic.
Exception as strings
In old versions of Python (Python 2.x), it was possible to raise an exception using a plain
string instead of an exception class. This was called “exceptions as strings.”
👉 Example (Python 2.x only):
raise "Something went wrong"
This would raise an exception where the exception itself was just a string.
🚫 Why it’s a Problem
• A string doesn’t have attributes like .args, .message, .with_traceback().
• It breaks the hierarchy of exceptions (no Exception class inheritance).
• Hard to handle with except properly.
✅ Python 3 (Modern Way)
In Python 3, exceptions must be instances of classes that inherit from BaseException (usually
Exception).
You cannot raise plain strings anymore.
👉 Correct way:
raise Exception("Something went wrong")
Summary
• Exception as strings → Old Python 2 feature, where exceptions could be simple string
values.
• Not allowed in Python 3 → now exceptions must be proper objects (class-based).
• Today, when we say “exception as string” in Python, it usually means getting the
message of an exception using str(e), not raising a raw string.
Python 2.x (Old Way — Strings Allowed)
# 🚫 Old Style: Raising exception as a string
try:
raise "Some error occurred" # string exception
except "Some error occurred": # catching by string
print("Caught string exception")
✅ Worked in Python 2
❌ Not allowed in Python 3
Python 3.x (Modern Way — Class Based Only)
# ✅ Correct Way: Use Exception class
try:
raise Exception("Some error occurred") # message is string
except Exception as e:
print("Caught exception:", e)
Output (Python 3):
Caught exception: Some error occurred
Key Differences
Feature Python 2 (String Exception) Python 3 (Class Exception)
Can raise raw string? ✅ Yes ❌ No
Inherits from Exception hierarchy? ❌ No ✅ Yes
Can use except Exception to catch? ❌ No ✅ Yes
Modern support Deprecated Standard
✅ Conclusion:
• “Exceptions as strings” was an old feature in Python 2 (deprecated).
• In Python 3, you must always raise exception objects (raise Exception("msg")).
• The string part is only the message of the exception, accessible with str(e).
“In Python 2 you could raise a string, but in Python 3 you must raise a thing (an Exception
object).”
So:
• String → Old (Python 2)
• Thing (class/object) → New (Python 3)
Raising exceptions
1. What Does “Raising an Exception” Mean?
• Normally, exceptions happen when Python detects an error (e.g.,
ZeroDivisionError).
• But sometimes you want to force an exception yourself when something goes
wrong in your logic.
• This is done using the raise statement.
2. Syntax
raise ExceptionType("Error message")
• ExceptionType → Any built-in or user-defined exception class
• "Error message" → A string that explains the error
3. Example: Raising a Built-in Exception
def withdraw(balance, amount):
if amount > balance:
raise ValueError("Insufficient funds!") # raising exception
return balance - amount
try:
print(withdraw(500, 1000))
except ValueError as e:
print("Caught error:", e)
Output:
Caught error: Insufficient funds!
4. Raising Without a Message
raise ZeroDivisionError
This raises the exception without any custom message.
5. Re-raising an Exception
You can re-raise the same exception inside an except block.
try:
x=1/0
except ZeroDivisionError:
print("Handling error, but re-raising...")
raise # raises the same exception again
6. Raising Custom Exceptions
You can create your own exception class and raise it.
class NegativeAgeError(Exception):
pass
age = -5
if age < 0:
raise NegativeAgeError("Age cannot be negative!")
✅ Summary
• Use raise to throw exceptions manually.
• Can raise built-in exceptions (like ValueError, TypeError).
• Can define and raise custom exceptions.
• Can re-raise exceptions inside except.
Raising Exceptions in Python – Quick Notes
Scenario Code Example Explanation
Raise a built-in
Raises a ValueError
exception (with python\nraise ValueError("Invalid input!")\n
with a custom message
message)
Raise a built-in
Raises
exception
python\nraise ZeroDivisionError\n ZeroDivisionError
(without
without details
message)
python\ndef check_age(age):\n if age < 0:\n raise
Raise inside a Function enforces rule
ValueError("Age cannot be
function using exception
negative!")\n\ncheck_age(-5)\n
python\ntry:\n x = 1 / 0\nexcept Handles exception
Re-raise inside
ZeroDivisionError:\n print("Caught but re- partially, then throws
except
raising...")\n raise\n again
python\nclass NegativeAgeError(Exception):\n
Custom User-defined exception
pass\n\nraise NegativeAgeError("Age must be
exception class for specific cases
positive!")\n
python\nfor val in [10, -1]:\n if val < 0:\n raise You can raise as many
Multiple raises
Exception("Negative number found!")\n times as needed
✅ Key Takeaways from Flowchart(Given Below)
1. raise sends control directly to the nearest matching except block.
2. If no handler is found → program terminates with error.
3. finally block (if present) always executes (whether exception handled or not).
Flow of Raising Exceptions in Python
┌────────────────────┐
│ Program Execution │
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ raise Exception │ ← you raise manually (or Python auto-raises)
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Look for matching │
│ except block │
└─────────┬──────────┘
Yes │ │ No
│ ▼
│ ┌────────────────────┐
│ │ Program crashes │
│ │ (Uncaught Exception) │
│ └────────────────────┘
│
▼
┌────────────────────┐
│ except block runs │ ← handles the exception
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ finally (if any) │ ← always runs (cleanup)
└─────────┬──────────┘
│
▼
┌────────────────────┐
│ Continue Program │
└────────────────────┘
Code Example
def divide(a, b):
if b == 0:
raise ZeroDivisionError("Cannot divide by zero!") # (1) raise
return a / b
try:
result = divide(10, 0) # (0) program execution
print("Result:", result)
except ZeroDivisionError as e: # (2) except block
print("Caught exception:", e)
finally: # (3) finally block
print("Finally block always runs")
print("Program continues...") # (4) continue program
Mapping to Flowchart Steps
1. (0) Program Execution → Call divide(10, 0)
2. (1) raise → raise ZeroDivisionError("Cannot divide by zero!")
3. (2) except block runs → "Caught exception: Cannot divide by zero!"
4. (3) finally block runs → "Finally block always runs"
5. (4) Program continues → "Program continues..."
Output
Caught exception: Cannot divide by zero!
Finally block always runs
Program continues...
✅ This shows exactly how raise → except → finally → continue matches the flowchart.
Examples
1. Raise with message
raise ValueError("Invalid input provided!")
2. Raise without message
raise ZeroDivisionError
3. Raise custom exception
class MyError(Exception):
pass
raise MyError("Something went wrong")
4. Re-raise an exception (inside except)
try:
x=1/0
except ZeroDivisionError:
print("Handling error but re-raising...")
raise
Assertions
Definition
An assertion is a debugging tool in Python.
• It tests a condition.
• If the condition is True, the program continues normally.
• If the condition is False, Python raises an AssertionError.
Syntax
assert condition, "Optional error message"
• condition → Expression that must be True.
• "Optional error message" → Message shown if the assertion fails.
Examples
1. Simple assertion
x = 10
assert x > 0 # Passes, since 10 > 0
print("Assertion passed")
✅ Output:
Assertion passed
2. Assertion fails
x = -5
assert x > 0, "x must be positive"
print("This will not run")
❌ Output:
AssertionError: x must be positive
3. Using in functions
def divide(a, b):
assert b != 0, "Denominator cannot be zero"
return a / b
print(divide(10, 2)) # Works
print(divide(5, 0)) # AssertionError
🔑 Key Points
1. Assertions are mainly for debugging/testing, not for handling runtime errors in
production.
2. They can be disabled globally by running Python with the -O (optimize) flag.
3. Use assert when you want to check assumptions in your code.
Difference between assert and raise in Python
Feature assert raise
Debugging tool to test Used to raise exceptions (built-in or
Purpose
assumptions during development custom)
Syntax assert condition, "message" raise ExceptionType("message")
Exception Always raises AssertionError if Raises specified exception (e.g.,
Raised condition is false ValueError, TypeError)
Quick checks in code, unit Enforcing input validation, error handling,
Use Case
testing, validating logic stopping execution
Can be disabled globally with -O
Disabling Cannot be disabled
(optimize flag)
Custom Not supported (always Supports custom exceptions (class
Exceptions AssertionError) MyError(Exception))
Example assert x > 0, "x must be positive" raise ValueError("x must be positive")
Standard Exceptions
Python provides many built-in exception classes, all of which inherit from the base class
Exception (which itself inherits from BaseException).
Common Standard Exceptions
Exception Raised When...
ValueError Invalid value is passed to a function (e.g., int("abc")).
TypeError Operation/function applied to an inappropriate type (e.g., "2" + 3).
IndexError Index is out of range in a list/tuple/string.
KeyError Dictionary key not found.
ZeroDivisionError Division or modulo by zero.
AttributeError Attribute reference fails (e.g., "abc".push()).
ImportError Importing a non-existent module/function/class.
A specific subclass of ImportError (when the module itself isn’t
ModuleNotFoundError
found).
FileNotFoundError Trying to open a file that doesn’t exist.
IOError Input/output operation fails (merged with OSError in Python 3).
OSError OS-related errors (e.g., file permission issues).
MemoryError Operation runs out of memory.
OverflowError Result of arithmetic operation is too large.
IndentationError Incorrect indentation in Python code.
SyntaxError Invalid Python syntax.
RuntimeError Error doesn’t fall into another category.
StopIteration Iterator is exhausted (used internally by for loops).
AssertionError assert statement fails.
KeyboardInterrupt User interrupts program with Ctrl + C.
SystemExit Raised by sys.exit() to stop program.
Example Demonstration
# ValueError
try:
num = int("abc") # invalid conversion
except ValueError as e:
print("Caught ValueError:", e)
# ZeroDivisionError
try:
result = 10 / 0
except ZeroDivisionError as e:
print("Caught ZeroDivisionError:", e)
# KeyError
try:
d = {"a": 1}
print(d["b"])
except KeyError as e:
print("Caught KeyError:", e)
Output
Caught ValueError: invalid literal for int() with base 10: 'abc'
Caught ZeroDivisionError: division by zero
Caught KeyError: 'b'
Python Exception Hierarchy (Simplified)
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ └── FileNotFoundError
├── RuntimeError
│ ├── RecursionError
│ └── NotImplementedError
├── StopIteration
├── StopAsyncIteration
├── SyntaxError
│ ├── IndentationError
│ └── TabError
├── TypeError
└── ValueError
Creating Exceptions
Python allows you to define your own exceptions in addition to using the standard ones.
Custom exceptions are useful when you want to signal specific errors related to your
application’s logic.
Steps to Create a Custom Exception
1. Create a new class that inherits from Exception (or a subclass of it).
2. (Optional) Add a custom __init__ or __str__ method to provide extra info.
3. Use the raise keyword to trigger your custom exception.
4. Use try-except to handle it.
✅ Example 1: Basic Custom Exception
# Step 1: Define a custom exception
class NegativeNumberError(Exception):
"""Raised when a number is negative."""
pass
# Step 2: Use it in code
def check_number(num):
if num < 0:
raise NegativeNumberError("Negative numbers are not allowed!")
return num
# Step 3: Handle it
try:
check_number(-5)
except NegativeNumberError as e:
print("Caught custom exception:", e)
Output:
Caught custom exception: Negative numbers are not allowed!
✅ Example 2: Custom Exception with Attributes
class AgeTooLowError(Exception):
def __init__(self, age, message="Age must be at least 18"):
self.age = age
self.message = message
super().__init__(self.message)
# Using the exception
def register_user(age):
if age < 18:
raise AgeTooLowError(age)
print("Registration successful!")
try:
register_user(15)
except AgeTooLowError as e:
print(f"Error: {e.message}. You entered: {e.age}")
Output:
Error: Age must be at least 18. You entered: 15
🔹 Key Points
• All custom exceptions should inherit from Exception (not BaseException, which is
for system-exiting errors).
• You can build a hierarchy of exceptions for complex apps.
• Add custom attributes to pass extra details about the error.
Example: Custom Exception for Division by Odd Number
• We’ll make a custom exception OddDivisorError that is raised if the divisor is an
odd number.
• Code
• # Custom Exception
• class OddDivisorError(Exception):
• """Raised when divisor is an odd number."""
• pass
•
• # Function that divides only by even numbers
• def safe_divide(a, b):
• if b % 2 != 0: # divisor is odd
• raise OddDivisorError("Divisor must be even!")
• return a / b
•
• # Testing
• try:
• result = safe_divide(10, 3)
• print("Result:", result)
• except OddDivisorError as e:
• print(" Error:", e)
•
• Output
• Error: Divisor must be even!
Why Exceptions(NOW)
Exceptions are needed because errors are natural in any program (like invalid inputs, missing
files, wrong calculations).
Instead of crashing the program, exceptions allow graceful error handling.
1. N → Natural Error Handling
Errors (like division by zero, file not found) are common.
Exceptions provide a natural mechanism to catch them instead of stopping execution.
👉 Example:
try:
print(10 / 0)
except ZeroDivisionError:
print("You cannot divide by zero!")
2. O → Organized Code
Without exceptions, we’d use too many if-else checks.
Exceptions make code clean, readable, and modular.
👉 Example:
# Without exception handling (messy)
if denominator != 0:
result = numerator / denominator
else:
print("Error")
# With exception handling (organized)
try:
result = numerator / denominator
except ZeroDivisionError:
print("Error")
3. W → Workflow Continuity
Even if an error happens, the program can continue instead of crashing.
This improves user experience and system stability.
👉 Example:
nums = [10, 0, 5]
for n in nums:
try:
print(10 / n)
except ZeroDivisionError:
print("Skipped zero divisor")
✅ Output:
1.0
Skipped zero divisor
2.0
Summary
Why Exceptions (NOW)?
• N → Natural way to deal with errors.
• O → Organized, clean code.
• W → Workflow continues smoothly.
✅ Full Code
print("Program Started")
try:
result = 10 / 0 # ❌ Error occurs
print("Result:", result)
except ZeroDivisionError:
print("❌ Error handled: Cannot divide by zero")
print("Program Continues Normally ✅")
🔹 Output
Program Started
❌ Error handled: Cannot divide by zero
Program Continues Normally ✅
Why exception at all?
In real programs, errors are inevitable (user mistakes, invalid inputs, missing files, wrong
operations). Without exceptions, programs would simply crash and stop execution.
Exceptions give us a systematic, clean, and safe way to deal with such errors.
1. Graceful Handling of Errors
Instead of crashing, we can catch the error and give a meaningful message.
👉 Example (without exception):
print(10 / 0) # Program crashes → ZeroDivisionError
👉 With exception handling:
try:
print(10 / 0)
except ZeroDivisionError:
print("❌ Cannot divide by zero!")
2. Cleaner Code than if-else
Without exceptions, we’d have to add lots of if-else checks.
Exceptions let us keep the code organized and readable.
3. Workflow Continuity
Even if an error happens, the rest of the program can continue running instead of stopping
completely.
👉 Example:
nums = [10, 0, 5]
for n in nums:
try:
print(10 / n)
except ZeroDivisionError:
print("Skipped division by zero")
print("✅ Program finished without crash")
4. Standardized Error Reporting
Python exceptions give us consistent error messages and types (like ValueError,
TypeError, FileNotFoundError), so developers immediately know what went wrong.
5. Separation of Normal Logic & Error Logic
• Normal flow of program stays clean.
• Error handling is separated into try-except.
This makes debugging & maintenance much easier.
🔹 Summary (Why Exceptions at all?)
• ✅ Handle errors gracefully instead of crashing.
• ✅ Make code cleaner and more organized.
• ✅ Allow program to continue execution.
• ✅ Provide standardized error reporting.
• ✅ Separate normal logic from error handling.
Exception and the sys module
1. Exceptions in Python
An exception in Python is an error that occurs during program execution, which disrupts the
normal flow of instructions.
Examples: dividing by zero, accessing an invalid index, or opening a file that doesn’t exist.
Common Built-in Exceptions:
• ZeroDivisionError → divide by zero
• IndexError → accessing invalid index in a list
• ValueError → invalid value for an operation
• TypeError → wrong data type used in an operation
• KeyError → key not found in a dictionary
Example:
try:
num = int(input("Enter a number: "))
result = 10 / num
print("Result:", result)
except ZeroDivisionError:
print("Error: Cannot divide by zero")
except ValueError:
print("Error: Invalid input, please enter a number")
finally:
print("Execution finished")
✅ try → code that may cause error
✅ except → handles the error
✅ finally → always executes
2. The sys Module in Python
The sys module provides functions and variables that interact with the Python interpreter.
It is widely used in exception handling, debugging, and program control.
Common Uses:
1. Exit a program
import sys
print("Before exit")
sys.exit() # terminates the program
print("This won't be printed")
2. Get exception details
import sys
try:
x=1/0
except:
print("Exception type:", sys.exc_info()[0])
print("Exception message:", sys.exc_info()[1])
Here, sys.exc_info() returns:
• Exception type
• Exception value (message)
• Traceback object
3. Get Python version & platform
import sys
print("Python Version:", sys.version)
print("Platform:", sys.platform)
4. Command-line arguments
import sys
print("Script name:", sys.argv[0])
print("Arguments:", sys.argv[1:])
If you run python script.py arg1 arg2 → argv will be ['script.py', 'arg1', 'arg2'].
✨ In short:
• Exceptions help in handling runtime errors gracefully.
• sys module helps in interacting with the Python interpreter and is often used in
exception handling (like sys.exc_info()) and program control.
✅ Key points in diagram:
• Exceptions interrupt normal flow.
• If handled, program continues.
• sys.exc_info() helps fetch detailed exception info.
• If no handler → program crashes.
Related Module
📘 Python Related Modules (with Uses)
Module Category Purpose / Uses
System / Interact with Python interpreter (argv, exit, version,
sys
Interpreter exc_info)
File & directory operations, environment variables,
os System / OS
process management
Get system/OS information (platform.system(),
platform System Info
platform.release())
subprocess System Processes Run external commands/programs from Python
shutil File Operations Copy, move, delete files & directories
Exception
traceback Get stack traces, format/print errors
Handling
Exception /
logging Log errors, warnings, info, and debug messages
Debugging
warnings Debugging Handle or suppress warnings
faulthandler Debugging Print traceback on fatal errors (low-level debugging)
io File Handling Input/Output streams, file-like object handling
pickle Persistence Serialize & deserialize Python objects
Module Category Purpose / Uses
json Data Handling Work with JSON data (encode/decode)
csv Data Handling Read/write CSV files
sqlite3 Database Built-in SQL database support
math Math Mathematical functions (sin, cos, sqrt, factorial, etc.)
Math / Data
statistics Mean, median, stdev, variance
Analysis
Math / Data
random Random numbers, shuffle, choice, sampling
Generation
decimal Math / Precision Decimal fixed-point arithmetic
fractions Math Rational numbers operations
time Date / Time Time handling, delays, performance measurement
datetime Date / Time Date/time manipulation, formatting
calendar Date / Time Generate & work with calendars
socket Networking Low-level network communication
http Networking HTTP client/server handling
urllib Networking URL handling, fetch web pages
requests
Networking Simplified HTTP requests (GET, POST, etc.)
(external)
Modules in Python
• A module is simply a Python file (.py) that contains code: functions, classes, or
variables.
• It allows code reusability and modular programming (breaking big programs
into smaller files).
✅ Example of a module (mymath.py):
# mymath.py
def add(a, b):
return a + b
def sub(a, b):
return a - b
✅ Using the module in another file:
# main.py
import mymath # importing our module
print(mymath.add(10, 5)) # 15
print(mymath.sub(10, 5)) # 5
🔹 We can also import specific functions:
from mymath import add
print(add(2, 3)) # 5
Types of Modules
1. Built-in Modules (already available in Python)
Example: math, os, random
2. import math
3. print(math.sqrt(16)) # 4.0
4. User-defined Modules (created by us, like mymath.py above)
5. Third-party Modules (need installation using pip)
Example: numpy, pandas
📌 Files in Python
Working with files is done using the open() function.
✅ File Modes
• "r" → read (default)
• "w" → write (overwrites file)
• "a" → append
• "b" → binary mode (e.g., images)
• "x" → create new file
✅ Example: Writing to a file
f = open("sample.txt", "w") # create or overwrite
f.write("Hello, Python!\n")
f.write("File handling is easy.")
f.close()
✅ Example: Reading from a file
f = open("sample.txt", "r")
content = f.read()
print(content)
f.close()
✅ Example: Appending to a file
f = open("sample.txt", "a")
f.write("\nAdding a new line.")
f.close()
📌 Best Practice: Using with
with open("sample.txt", "r") as f:
for line in f:
print(line.strip())
🔹 This automatically closes the file after use.
✨ So in short:
• Modules = reusable Python files containing functions, classes, variables.
• Files = reading/writing external data (like text, CSV, JSON).
Namespaces
In Python, a namespace is like a container (or a mapping) that holds names (identifiers like
variables, functions, classes, etc.) and the objects they refer to.
Think of it as a dictionary where keys are names and values are objects.
Types of Namespaces in Python
Namespaces exist at different levels:
1. Built-in Namespace
o Contains names that are always available in Python, like len(), print(), int,
Exception, etc.
o Created when the Python interpreter starts.
2. print(len("Hello")) # 'len' is in the built-in namespace
3. Global Namespace
o Names defined at the top-level of a module or script.
o Includes functions, variables, and imported modules.
4. x = 10 # global namespace
5. def my_func():
6. pass
7. Enclosing Namespace (also called Nonlocal)
o Names in the outer function for nested functions.
o Used in closures.
8. def outer():
9. y = 20
10. def inner():
11. print(y) # 'y' is from enclosing namespace
12. inner()
13. outer()
14. Local Namespace
o Names inside a function (local variables, parameters).
o Created when the function is called and destroyed when the function
exits.
15. def func():
16. z = 30 # local namespace
17. print(z)
18. func()
The LEGB Rule
Python resolves names by searching in this order:
1. Local → inside the current function.
2. Enclosing → in any enclosing functions.
3. Global → at the top-level of the module.
4. Built-in → Python’s built-in names.
Example:
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # LEGB: local → enclosing → global → built-in
inner()
outer() # Output: local
Inspecting Namespaces
You can view namespaces as dictionaries:
print(globals().keys()) # global namespace
print(locals().keys()) # local namespace (inside a function)
In short:
• Namespace avoids naming conflicts.
• Python looks for a name in LEGB order.
• Different scopes (local, enclosing, global, built-in) each have their own
namespace.
IMPORTING MODULE IN PYTHON AND IMPORTING MODULE ATTRIBUTES
Importing Modules in Python
A module is just a Python file (.py) that contains functions, classes, or variables.
We can import it into another program to reuse the code.
1. Importing a Whole Module
import math # importing entire module
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
Here, you must use the module name (math.) before attributes.
2. Importing with an Alias
import math as m
print(m.sqrt(25)) # 5.0
Aliases make code shorter and cleaner.
3. Importing Specific Attributes
from math import sqrt, pi
print(sqrt(49)) # 7.0
print(pi) # 3.141592653589793
No need to write math. before sqrt or pi.
4. Importing All Attributes
from math import *
print(sin(0)) # 0.0
print(cos(0)) # 1.0
Not recommended (may cause conflicts if two modules have the same function
names).
5. Importing User-Defined Module
Suppose we created a module mymath.py:
# mymath.py
def add(a, b):
return a + b
def sub(a, b):
return a – b
Now, in another file:
import mymath
print(mymath.add(10, 5)) # 15
print(mymath.sub(10, 5)) # 5
Or import only specific attributes:
from mymath import add
print(add(7, 3)) # 10
Summary
• import module → imports everything, access with module.name
• import module as alias → imports everything, access with alias
• from module import name → imports only selected attributes
• from module import * → imports all attributes (not recommended)
Module built in function, packages and other features of modules
Expanded Notes on Modules in Python
1. Modules
• A module is a Python file (.py) that contains functions, variables, classes, etc.
• Benefits:
o Code reusability
o Organized structure
o Easier debugging
o Namespace isolation
2. Built-in Functions Related to Modules
1. dir(module) → Lists all attributes (functions, classes, variables) inside the
module.
2. import math
3. print(dir(math)) # shows all names in math module
4. help(module) → Displays documentation of the module.
5. import random
6. help(random)
7. __name__ → Special attribute:
o If the file is run directly → "__main__"
o If the file is imported → module name
8. # demo.py
9. if __name__ == "__main__":
10. print("Running directly")
11. else:
12. print("Imported as a module")
13. __file__ → Path of the module file (not available for built-in modules).
14. import os
15. print(os.__file__)
3. Packages
• A package is a collection of modules arranged in directories.
• __init__.py makes Python treat a directory as a package.
• Packages can be nested (package inside another package).
Example:
mypackage/
│
├── __init__.py
├── mathops.py
└── stats.py
from mypackage import mathops
print(mathops.add(10, 20))
4. Features of Modules
1. Namespace Management
o Each module has its own namespace.
o Prevents name conflicts across files.
2. Reloading Modules
3. import importlib
4. import mymath
5. importlib.reload(mymath) # reloads without restarting Python
6. Types of Modules
o Built-in modules: math, os, sys, random
o User-defined modules: custom .py files
o Third-party modules: install via pip (numpy, pandas)
7. Standard Library Advantages
o Comes with 200+ modules
o Covers file handling, databases, networking, math, system operations, etc.
o Example:
o import datetime
o print(datetime.datetime.now())
8. Third-Party Module Usage
o Install: pip install module_name
o Example:
o import numpy as np
o arr = np.array([1, 2, 3, 4])
o print(np.mean(arr))
Quick Recap
• Modules: Python files containing reusable code
• Built-in functions: dir(), help(), __name__, __file__
• Packages: Collection of modules with __init__.py
• Features: Namespace, reloading, standard library, third-party libraries
Comparison of Modules and Packages in Python
Aspect Built-in Modules User-defined Modules Packages
Predefined modules that Python files (.py) Collection of modules
Definition
come with Python created by the user arranged in directories
math, os, random, sys, numpy, pandas,
Examples mymath.py, student.py
datetime mypackage/
Already available in Created manually by Create a folder with
Creation
Python installation writing Python code modules + __init__.py
Import from mypackage import
import math import mymath
Method module
Organizing large
Provides ready-made Custom functionalities
Use Case projects into structured
functionalities for specific projects
units
Third-party packages
Installation No installation required No installation required
require pip install
General-purpose (math, Task-specific (as per Large-scale project
Scope
system, I/O, etc.) developer’s need) management