# File Handling: File Types; Operations on Files– Create, Open, Read,
Write,
# Close Files; File Names and Paths; Format Operator.
# Definition:
# File handling allows you to interact with files stored on your
computer,
# enabling operations like reading data from existing files, writing
new data
# to files, and modifying file content.
# File Types:
# - Text Files (.txt, .csv, .py, .html): Store data as human-readable
characters.
# Each line typically ends with a newline character ('\n').
# - Binary Files (.jpg, .exe, .dat): Store data in a
non-human-readable format,
# representing raw bytes of information.
# Operations on Files: Create, Open, Read, Write, Close Files:
print("\n--- File Handling ---")
# 1. Opening a File:
# Syntax: open(filename, mode)
# - filename: A string specifying the name of the file (can include
the path).
# - mode: A string indicating the operation to be performed:
# - 'r': Read mode (default). Opens the file for reading. Error if
the file does not exist.
# - 'w': Write mode. Opens the file for writing. Creates a new file
if it does not exist or overwrites the existing file.
# - 'a': Append mode. Opens the file for writing, but adds new data
at the end of the file. Creates a new file if it does not exist.
# - 'b': Binary mode. Used with other modes (e.g., 'rb', 'wb') to
handle binary files.
# - '+': Updating mode. Allows both reading and writing (e.g., 'r+',
'w+', 'a+').
# Example: Opening a text file for reading
try:
file_read = open("sample.txt", "r")
print("File 'sample.txt' opened for reading.")
file_read.close()
except FileNotFoundError:
print("Error: 'sample.txt' not found.")
# Example: Opening a text file for writing (creates or overwrites)
try:
file_write = open("output.txt", "w")
file_write.write("This is the first line.\n")
file_write.close()
print("File 'output.txt' written successfully.")
except Exception as e:
print(f"Error writing to file: {e}")
# 2. Reading from a File:
# - read(): Reads the entire content of the file as a single string.
# - readline(): Reads a single line from the file, including the
newline character.
# - readlines(): Reads all lines from the file and returns them as a
list of strings.
# Example: Reading the entire content
try:
file = open("output.txt", "r")
content = file.read()
print(f"\nContent of 'output.txt':\n{content}")
file.close()
except FileNotFoundError:
print("Error: 'output.txt' not found.")
# Example: Reading line by line
try:
file = open("output.txt", "r")
print("\nReading 'output.txt' line by line:")
line1 = file.readline()
print(f"Line 1: {line1.strip()}")
line2 = file.readline()
print(f"Line 2: {line2.strip()}")
file.close()
except FileNotFoundError:
print("Error: 'output.txt' not found.")
# Example: Reading all lines into a list
try:
file = open("output.txt", "r")
lines = file.readlines()
print(f"\nAll lines in 'output.txt': {lines}")
file.close()
except FileNotFoundError:
print("Error: 'output.txt' not found.")
# 3. Writing to a File:
# - write(string): Writes the given string to the file. Newlines must
be explicitly included ('\n').
# - writelines(list_of_strings): Writes a list of strings to the file.
# Example: Writing multiple lines
try:
file = open("log.txt", "w")
lines_to_write = ["Error occurred at 10:00 AM\n", "Warning: Low
memory at 10:15 AM\n"]
file.writelines(lines_to_write)
file.close()
print("Data written to 'log.txt'.")
except Exception as e:
print(f"Error writing to 'log.txt': {e}")
# 4. Appending to a File:
try:
file = open("log.txt", "a")
file.write("Information: System started at 9:00 AM\n")
file.close()
print("Data appended to 'log.txt'.")
except Exception as e:
print(f"Error appending to 'log.txt': {e}")
# 5. Closing a File:
# - close(): Closes the file. It's crucial to close files after you're
done
# with them to release system resources and ensure data is written
to disk.
# - Using the 'with' statement: Provides a cleaner way to handle
files. It
# automatically closes the file even if errors occur.
# Example: Using the 'with' statement
with open("temp.txt", "w") as file:
file.write("This file will be automatically closed.")
print("File 'temp.txt' written and automatically closed.")
try:
with open("temp.txt", "r") as file:
content = file.read()
print(f"\nContent of 'temp.txt': {content}")
except FileNotFoundError:
print("Error: 'temp.txt' not found.")
# File Names and Paths:
print("\n--- File Names and Paths ---")
import os
# - Absolute Path: Specifies the exact location of a file or directory
on the system.
# Example (Unix-like): /home/user/documents/my_document.txt
# Example (Windows): C:\Users\User\Documents\my_document.txt
# - Relative Path: Specifies the location relative to the current
working directory.
# - 'my_file.txt': Refers to a file in the same directory.
# - 'data/info.csv': Refers to a file in a subdirectory named
'data'.
# - '../report.pdf': Refers to a file in the parent directory.
print(f"Current working directory: {os.getcwd()}")
# Format Operator:
print("\n--- Format Operator ---")
# The format operator (%) can be used to create formatted strings,
including
# those used for filenames or data written to files.
name = "Report_04_26_2025.txt"
print(f"Filename: {name}")
data = ("temperature", 25.5, "humidity", 60)
log_message = "Measurement: %s = %.2f, %s = %d" % data
print(log_message)
# Object Oriented Programming: Classes and Objects; Creating Classes
and
# Objects; Constructor Method; Classes with Multiple Objects; Objects
as
# Arguments; Objects as Return Values; Inheritance- Single and
Multiple
# Inheritance, Multilevel and Multipath Inheritance; Encapsulation-
Definition,
# Private Instance Variables; Polymorphism- Definition, Operator
Overloading.
# Classes and Objects:
# Definition:
# - Class: A blueprint or template for creating objects. It defines
the
# attributes (data) and methods (functions) that objects of that
class will have.
# - Object (Instance): A specific instance of a class. It has its own
set of
# values for the attributes defined by the class.
# Creating Classes and Objects:
print("\n--- Object Oriented Programming ---")
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius * self.radius
def circumference(self):
return 2 * 3.14159 * self.radius
# Creating Objects:
circle1 = Circle(5)
circle2 = Circle(10)
print(f"Circle 1 - Radius: {circle1.radius}, Area:
{circle1.area():.2f}, Circumference: {circle1.circumference():.2f}")
print(f"Circle 2 - Radius: {circle2.radius}, Area:
{circle2.area():.2f}, Circumference: {circle2.circumference():.2f}")
# Constructor Method (__init__):
# Definition:
# The __init__ method is a special method called the constructor. It
is
# automatically called when an object of the class is created. It is
used to
# initialize the object's attributes.
# Syntax:
# def __init__(self, parameter1, parameter2, ...):
# self.attribute1 = parameter1
# self.attribute2 = parameter2
# ...
# Example: (See the Circle class above)
# Classes with Multiple Objects:
# Definition:
# A class can be used to create multiple independent objects
(instances), each
# with its own set of attribute values.
# Example: (circle1 and circle2 are multiple objects of the Circle
class)
# Objects as Arguments:
# Definition:
# Objects can be passed as arguments to methods of a class or to
regular functions.
# Example:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def compare_areas(rect1, rect2):
if rect1.area() > rect2.area():
print("Rectangle 1 has a larger area.")
elif rect2.area() > rect1.area():
print("Rectangle 2 has a larger area.")
else:
print("Both rectangles have the same area.")
rect_a = Rectangle(5, 10)
rect_b = Rectangle(8, 6)
compare_areas(rect_a, rect_b)
# Objects as Return Values:
# Definition:
# Methods of a class or regular functions can return objects as their
result.
# Example:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def move(self, dx, dy):
self.x += dx
self.y += dy
return self # Return the modified object
p1 = Point(1, 2)
p2 = p1.move(3, 4)
print(f"Point 1: ({p1.x}, {p1.y})")
print(f"Point 2 (returned object): ({p2.x}, {p2.y})")
# Inheritance:
# Definition:
# Inheritance is a mechanism in OOP where a new class (derived or
child class)
# inherits properties (attributes and methods) from an existing class
(base or
# parent class). This promotes code reusability.
# Single Inheritance:
# Definition:
# A derived class inherits from only one base class.
# Syntax:
# class DerivedClass(BaseClass):
# # Class body
# Example:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print("Generic animal sound")
class Dog(Animal):
def speak(self):
print("Woof!")
my_dog_inh = Dog("Buddy")
print(f"Dog's name: {my_dog_inh.name}")
my_dog_inh.speak()
# Multiple Inheritance:
# Definition:
# A derived class inherits from more than one base class.
# Syntax:
# class DerivedClass(BaseClass1, BaseClass2, ...):
# # Class body
# Example:
class Swimmer:
def swim(self):
print("Swimming")
class Walker:
def walk(self):
print("Walking")
class Amphibian(Swimmer, Walker):
pass
frog = Amphibian()
frog.swim()
frog.walk()
# Multilevel Inheritance:
# Definition:
# A derived class inherits from another derived class, forming a
hierarchy.
# Example:
class Grandparent:
def has_wisdom(self):
print("Grandparent has wisdom.")
class Parent(Grandparent):
def has_experience(self):
print("Parent has experience.")
class Child(Parent):
def is_curious(self):
print("Child is curious.")
child_obj = Child()
child_obj.has_wisdom()
child_obj.has_experience()
child_obj.is_curious()
# Multipath Inheritance:
# Definition:
# A form of multiple inheritance where a derived class inherits from
two or more
# base classes that have a common ancestor. This can lead to the
"diamond problem"
# if the common ancestor has methods that are not overridden. Python's
Method
# Resolution Order (MRO) helps resolve this.
# Example:
class A:
def show(self):
print("Show from A")
class B(A):
pass
class C(A):
def show(self):
print("Show from C")
class D(B, C):
pass
d_obj = D()
d_obj.show() # Output will be "Show from C" due to MRO
# Encapsulation:
# Definition:
# Encapsulation is the bundling of data (attributes) and the methods
that
# operate on that data within a single unit (class). It helps in
hiding the
# internal implementation details of an object and provides a
controlled
# interface for accessing and modifying its data.
# Private Instance Variables:
# Definition:
# In Python, instance variables can be made "private" (though not
strictly enforced)
# by prefixing their names with a double underscore (`__`). This
signals that
# they should not be directly accessed or modified from outside the
class.
# Example:
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private instance variable
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def withdraw(self, amount):
if amount > 0 and amount <= self.__balance:
self.__balance -= amount
return True
return False
def get_balance(self):
return self.__balance
account = BankAccount(1000)
account.deposit(500)
account.withdraw(200)
print(f"Account balance: {account.get_balance()}")
# print(account.__balance) # This would raise an AttributeError
# Polymorphism:
# Definition:
# Polymorphism (meaning "many forms") allows objects of different
classes to
# respond to the same method call in their own way. It enables you to
write
# code that can work with objects of different types without needing
to know
# their specific class.
# Operator Overloading:
# Definition:
# Operator overloading allows you to redefine the behavior of built-in
operators
# (e.g., +, -, *, ==) for objects of your custom classes.
# Example:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __str__(self):
return f"({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # Uses the overloaded __add__ method
print(f"Vector 1: {v1}")
print(f"Vector 2: {v2}")
print(f"Vector 3 (v1 + v2): {v3}")