Python Assignment One PDF
Python Assignment One PDF
Submitted by
ANAND RAJ(1DS24IS020)
CERTIFICATE
This is to certify that the Alternate Assessment Tool (AAT) submitted as part of Python
Programming (ESC22IS362) is a bonafide work carried out by Anand Raj (1DS24IS020) as 10-mark
component in partial fulfillment for the 3rd semester of Bachelor of Engineering in Information Science
& Engineering of the Visvesvaraya Technological University, Belgaum during the year 2025-26. The
AAT report has been approved as it satisfies the academic requirements prescribed for the Bachelor of
Engineering degree.
DECLARATION
I declare that I abide by the ethical principles and commit to professional ethics and
responsibilities and norms of the engineering practice. The work submitted in this report of Python
Programming (ESC22IS362) III Semester BE, ISE has been compiled by referring to the relevant online
and offline resources to the best of my understanding and in partial fulfillment of the requirement for the
award of the degree of Bachelor of Engineering in Information Science & Engineering, at Dayananda
Sagar College of Engineering, an autonomous institution affiliated to VTU, Belagavi during the academic
year 2025-26.
I hereby declare that the same has not been submitted in part or full for other academic purposes.
Place: Bengaluru
PART A
a) Program
def main():
filename = "input_data.txt"
with open(filename, "w") as f:
[Link]("The racecar driver saw a level radar.\n")
[Link]("Madam, the rotor is broken.\n")
[Link]("A man, a plan, a canal, Panama? No, just a malayalam word.")
all_palindromes = []
try:
with open(filename, "r") as file:
lines = [Link]()
for line in lines:
found = get_palindromes([Link](), min_length=5)
all_palindromes.extend(found)
except FileNotFoundError:
print("Error: The file was not found.")
if __name__ == "__main__":
main()
a) Program
import sys
def main():
while True:
print("\n--- String Operations Menu ---")
print("1. Convert to Uppercase")
print("2. Count Character Occurrences")
print("3. Check Prefix")
print("4. Exit")
if choice == '1':
text = input("Enter a string: ")
print("Result:", [Link]())
else:
print("Invalid choice, please try again.")
if __name__ == "__main__":
main()
Program Logic: The program uses an infinite while loop to continuously display the menu until the user explicitly
chooses to exit. It utilizes input() to capture the user's string and choice. Depending on the selection, it calls the
standard string methods directly on the input variable.
Justification for Built-in Methods: Using built-in methods like upper(), count(), and startswith() is significantly
more efficient than writing manual for loops in Python for three key reasons:
1. C Implementation: Python's built-in string methods are implemented in C. When you call [Link](), the
operation runs at C-speed, which is much faster than processing a Python loop that executes bytecode
instruction by instruction.
a) Program
def process_stats(data_list):
try:
if not isinstance(data_list, list):
raise TypeError("Input provided is not a list.")
unique_list = []
for item in data_list:
if not isinstance(item, int):
raise ValueError(f"Non-integer value '{item}' found.")
unique_list.sort()
n = len(unique_list)
if n == 0:
print("List is empty.")
return
total = sum(unique_list)
mean_val = total / n
if n % 2 == 1:
median_val = unique_list[n // 2]
else:
mid1 = unique_list[n // 2 - 1]
mid2 = unique_list[n // 2]
median_val = (mid1 + mid2) / 2
def main():
print("--- Case 1: Valid Input with Duplicates ---")
numbers = [10, 2, 5, 10, 8, 2, 20]
process_stats(numbers)
DAA (IPCC22IS43) AY 2025-26
print(f"Original List Check: {numbers}") Page 6
numbers = [10, 2, 5, 10, 8, 2, 20]
process_stats(numbers)
print(f"Original List Check: {numbers}")
print("\n--- Case 2: Invalid Input ---")
mixed_data = [10, 20, "thirty", 40]
process_stats(mixed_data) if __name__ == "__main__": main()
b) Output:-
Logic: The function first validates the input type. It iterates through the list, checking if each element
is an integer (raising a ValueError if not) and appending it to a new list unique_list only if it hasn't been
added yet (removing duplicates). It then sorts this new list.
• Mean: Calculated by summing the unique elements and dividing by the count.
• Median: Determined by finding the middle index. If the length is even, it averages the two
middle numbers.
• Mode: Since duplicates are removed before calculation, every number appears exactly once.
Thus, there is no single mode (uniform distribution).
Analysis: List Mutability:
• Python Lists are Mutable: This means their content can be changed in place.
• Function Behavior: In the code above, we created a new list (unique_list) rather than modifying
the original data_list.
o If we had used data_list.sort() or data_list.remove(), these changes would reflect in the
main block variable numbers because lists are passed by object reference.
o By creating a new variable, we avoided "side effects," keeping the original data intact
(as seen in the output Original List Check). This is generally preferred in functional
programming to prevent data corruption.
a) Program
def add_student(records, roll, marks):
# Check if roll already exists
for r, m in records:
if r == roll:
print(f"Error: Roll {roll} already exists.")
return
[Link]((roll, marks))
print(f"Added: Roll {roll}, Marks {marks}")
def main():
# List of tuples structure
student_records = []
search_student(student_records, 102)
delete_student(student_records, 102)
'''
--- EVALUATION & JUSTIFICATION ---
DAA (IPCC22IS43) AY 2025-26 Page 8
1. Appropriateness:
Dictionaries are significantly more appropriate than a list of tuples
--- EVALUATION & JUSTIFICATION ---
1. Appropriateness:
Dictionaries are significantly more appropriate than a list of tuples
for this specific use case (associating unique keys to values).
2. Justification (Performance):
- List of Tuples: To find a student (Search, Update, or Delete),
we must iterate through the list using a loop.
This results in O(N) time complexity (Linear Time).
- Dictionary: A dictionary uses hashing. Looking up a key (Roll No)
does not require a loop. It happens in O(1) time complexity (Constant Time).
3. Conclusion:
For large datasets, the Dictionary is vastly superior in speed and
cleaner in syntax (e.g., records[roll] = marks).
'''
if __name__ == "__main__":
main()
b) Output :
• Conclusion: While a list of tuples works, a dictionary should always be the preferred
choice for record management based on unique IDs.
a) Program
import json
if found_books:
# Pretty printing using JSON module
print([Link](found_books, indent=4))
else:
print("No books found for this author.")
def main():
# Main dictionary to hold the library data
library_system = {}
# 1. Adding Books
add_book(library_system, "B001", "The Great Gatsby", "F. Scott Fitzgerald", 1925)
add_book(library_system, "B002", "1984", "George Orwell", 1949)
add_book(library_system, "B003", "Animal Farm", "George Orwell", 1945)
add_book(library_system, "B004", "Clean Code", "Robert Martin", 2008)
# 2. Searching by Author
search_by_author(library_system, "George Orwell")
if __name__ == "__main__":
main()
1. Hierarchical Representation
Real-world data is rarely flat. Objects often contain other objects. Nested dictionaries allow us to model
this hierarchy naturally. In this program, the structure is:
Library (System) -> Book (Entity) -> Attributes (Title, Author, Year)
This structure mirrors standard data formats like JSON (JavaScript Object Notation), which is the
universal standard for exchanging data between servers and web applications.
2. Efficient Access (O(1))
By using a dictionary keyed by BookID, we can access any specific book record instantly without looping
through the entire collection. If we used a list, finding a specific book ID would require checking every
item one by one ($O(N)$), which is inefficient for large libraries.
3. Flexibility
Dictionaries are schema-less. If we needed to add a new field (e.g., "Genre" or "ISBN") to just one book,
we could do so easily without breaking the structure of the other records. This flexibility is essential for
handling real-world data where information might be incomplete or variable.
Code Explanation
• add_book Function:
o This function accepts the library dictionary and book details.
o It first checks if the book_id already exists to prevent overwriting data.
o If the ID is unique, it creates a nested dictionary containing title, author, and year, and assigns
it to the book_id key in the main library dictionary.
• search_by_author Function:
o This function iterates through the library using .items(), which gives us both the bid (Book ID)
and details (the nested dictionary).
o It compares the stored author name with the requested name (converting both to lowercase to
make the search case-insensitive).
o If a match is found, that specific book is added to a temporary found_books dictionary, which
is then printed using [Link]
• [Link](obj, indent=4):
This is used for "Pretty Printing." It takes a standard Python dictionary and converts it into a
formatted string with 4 spaces of indentation, making the nested structure readable for humans.
a) Program
import re
def extract_data(text):
# Regex pattern for Email
# Matches alphanumeric characters, followed by @, domain name, and extension
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
def main():
paragraph = """
Please submit your project by 15-04-2024. If you have questions,
contact support@[Link] or [Link]@[Link] before the
deadline on 20/04/2024. Do not email invalid@addresses.
"""
extract_data(paragraph)
if __name__ == "__main__":
main()
b) Output :-
a) Program
class BankAccount:
def __init__(self, name, initial_balance=0):
[Link] = name
[Link] = initial_balance
def show_balance(self):
print(f"Account: {[Link]} | Balance: {[Link]}")
def main():
acc = BankAccount("John Doe", 1000)
acc.show_balance()
[Link](500)
acc.show_balance()
[Link](200)
acc.show_balance()
[Link](5000)
[Link](-100)
if __name__ == "__main__":
main()
• The program defines a Class named BankAccount, which serves as a blueprint for creating individual
account objects. The __init__ method is a special constructor that runs automatically when a new
object is created; it initializes the object's distinct state (its name and balance) using the self keyword.
self is crucial because it differentiates between the specific object being called (e.g., John's account
vs. Jane's account).
• The methods deposit and withdraw are the primary ways to interact with the account. Instead of just
doing math, these methods include logic. For instance, withdraw doesn't just subtract money; it first
checks if the requested amount is valid (greater than zero) and if sufficient funds exist. This logic
ensures the method handles the data intelligently. Finally, show_balance provides a read-only view of
the current state without modifying it.
1. Controlled Access (Data Protection) : Encapsulation restricts direct access to an object's internal state.
Instead of allowing external code to modify balance directly (e.g., [Link] = -500), interaction happens
through methods (deposit, withdraw). This ensures that the balance cannot be set to an invalid state (like a
negative number) or modified unpredictably.
2. Validation Logic By bundling the data with the methods that operate on it, we can enforce rules. In the
program above, the withdraw method includes logic to check if amount <= [Link]. This validation logic
protects the integrity of the data, ensuring the program behaves reliably even when bad inputs (like
withdrawing more than you own) are attempted.
a) Program
import shelve
import pprint
from collections import Counter
import ast
def count_words(filename):
with open(filename, 'r') as f:
text = [Link]().lower().replace('\n', ' ')
words = [Link]()
return dict(Counter(words))
def main():
input_file = "[Link]"
with open(input_file, "w") as f:
[Link]("apple banana apple cherry banana apple date")
word_counts = count_words(input_file)
shelve_file = "my_shelve_db"
save_with_shelve(word_counts, shelve_file)
pprint_file = "my_pprint_data.txt"
save_with_pprint(word_counts, pprint_file)
if __name__ == "__main__":
main()
The program begins by importing necessary libraries: shelve for persistent storage, pprint for formatting,
Counter for easy counting, and ast for safely evaluating strings back into Python objects. It creates a dummy
text file to ensure data exists. The count_words function reads this file, normalizes the text to lowercase, and
uses Counter to create a frequency dictionary (e.g., {'apple': 3}).
For storage, the program uses two distinct approaches. The save_with_shelve function opens a binary file that
acts like a persistent dictionary. It iterates through the word count dictionary and stores every item directly
onto the disk using keys. Conversely, save_with_pprint converts the dictionary into a human-readable string
format using pformat and writes that string into a standard text file.
For retrieval, the difference in logic is significant. retrieve_shelve simply opens the database and asks for a
specific key. retrieve_pprint, however, must open the text file, read the entire string into memory, and then
use ast.literal_eval to parse that string back into a real Python dictionary before it can look up the key.
1. Access Method:
o Shelve: Acts as a persistent, disk-based hash map. It supports Random Access, meaning it
can jump directly to the specific key you want without loading the entire dataset into RAM.
Its complexity for retrieval is close to $O(1)$.
o Pprint: Stores data as a flat text string. To retrieve even a single item, the program must read
the entire file ($O(N)$) and parse the whole string back into a Python object.
Conclusion: Use pprint when a human needs to read the file (debugging/logging). Use shelve (or databases)
when the program needs to store and retrieve data efficiently.
a) Program :-
import requests
from bs4 import BeautifulSoup
def get_hyperlinks(url):
try:
response = [Link](url)
response.raise_for_status()
return links
except [Link] as e:
print(f"Error fetching URL: {e}")
return []
def main():
target_url = "[Link]
all_links = get_hyperlinks(target_url)
if __name__ == "__main__":
main()
The program begins by importing the necessary libraries: requests for making HTTP calls and
BeautifulSoup from bs4 for parsing HTML.
The core logic is encapsulated in the get_hyperlinks function. It first executes a [Link](url) call to
download the webpage. The raise_for_status() method is immediately called to check for HTTP errors
(like 404 Not Found or 500 Server Error); if the request was unsuccessful, this raises an exception to halt
execution or trigger error handling.
Once the page content is successfully retrieved, BeautifulSoup parses the raw HTML text. The parser
creates a parse tree, which acts as a navigable structure of the HTML tags. The script uses
soup.find_all('a') to locate every anchor tag (<a>) in the document.
The loop iterates through these anchor tags. Crucially, we access the hyperlink using [Link]('href') rather
than standard dictionary access (tag['href']). This method returns None if the attribute is missing,
preventing the program from crashing on broken tags. Valid links are appended to the links list, which is
returned to the main function for printing.
When scraping real-world websites, the HTML is rarely perfect. Here are the primary challenges:
A. Malformed HTML Browsers are very forgiving and will render pages even if tags are not closed
properly (e.g., a <div> missing its </div>). However, strict parsers may fail to read this.
• Impact: If the HTML structure is broken, the parser might misinterpret where one element ends
and another begins. This can cause find_all to return fewer items than expected because they are
"swallowed" inside a broken tag.
• Solution: BeautifulSoup is robust because it uses lenient parsers (like [Link] or lxml). These
parsers attempt to "fix" the HTML by guessing where closing tags should be, allowing the scrape
to continue even on poorly coded sites.
B. Missing Tags and Attributes Not all anchor tags contain links. Some might be used as anchors for
jumping to parts of a page (e.g., <a name="top"></a>) or for JavaScript triggers (<a onclick="...">).
• Impact: A naive script accessing tag['href'] directly will crash with a KeyError if the attribute is
missing.
• Solution: Using the .get('href') method is essential. It safely returns None instead of raising an
error, allowing the script to use a simple if href: check to filter out empty or invalid tags.
C. Dynamic Content The requests library only fetches the initial static HTML sent by the server.
• Impact: Many modern sites load links using JavaScript after the page loads (Client-Side
Rendering). In these cases, soup.find_all will find nothing because the tags simply don't exist in
the initial response.
• Solution: This requires advanced tools like Selenium or Playwright that launch a real browser to
execute the JavaScript before scraping.
a) Program :-
import pandas as pd
import [Link] as plt
def main():
data = {
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eva'],
'Math': [85, 70, 95, 60, 88],
'Science': [90, 75, 85, 55, 92],
'English': [88, 65, 90, 60, 85]
}
df_temp = [Link](data)
df_temp.to_csv('student_marks.csv', index=False)
df = pd.read_csv('student_marks.csv')
def get_grade(avg):
if avg >= 90: return 'A'
elif avg >= 80: return 'B'
elif avg >= 70: return 'C'
elif avg >= 60: return 'D'
else: return 'F'
df['Grade'] = df['Average'].apply(get_grade)
print(df)
[Link](figsize=(8, 5))
[Link](df['Name'], df['Average'], color='skyblue')
[Link]('Student Name')
[Link]('Average Marks')
[Link]('Student Performance')
[Link]()
if __name__ == "__main__":
main()
c) Graph : -
Explanation : -
The program begins by importing the pandas library for data manipulation and [Link] for
visualization. Although the problem implies reading an existing CSV, the code includes a block to generate
a sample student_marks.csv file first to ensure the program runs successfully in any environment.
The core logic uses pd.read_csv to load the data into a DataFrame, which is the central data structure in
Pandas (essentially a programmable Excel sheet).
Calculations are performed using vectorized operations. Instead of looping through each student row by
row (as you would with standard Python lists), df['Math'] + df['Science'] adds the entire columns together
in a single step to compute the 'Total'. Similarly, division by 3 computes the 'Average' for the whole column
instantly.
For the 'Grade' classification, we define a helper function get_grade containing the conditional logic (A, B,
C, etc.). The .apply() method is then used to map this function to every value in the 'Average' column.
Finally, [Link] creates a bar chart using the 'Name' column for the x-axis and the computed 'Average'
column for the y-axis, providing a visual comparison of student performance.
Pure Python: To calculate the total marks for 1,000 students using lists, you must write a for loop that
iterates 1,000 times, extracting and adding values one by one. This is slow and verbose.
Pandas: Uses Vectorization. Operations like df['col1'] + df['col2'] are applied to the entire array at once
using highly optimized C-code in the background. This is significantly faster and requires much less
code.
3. Built-in Functionality
Pandas comes with built-in methods for file I/O (read_csv, to_excel), statistics (mean, describe),
and handling missing data (dropna), which would require writing extensive, error-prone utility
functions if done with pure Python lists.