Syllabus
Course Code: MCA504 • 9 Class Hours

Unit 2: Data Structure and File Handling

Master idiomatic Python comprehensions (List & Dictionary), robust File I/O with context managers, operating system interactions via os and sys, and CSV data streaming.

TU Marks Weightage: Approximately 10 to 15 Marks (Comprehensions, File Context Manager & CSV Reading Programs)

01. List Operations & List Comprehensions

A List in Python is a mutable, ordered, dynamic sequence. Lists support subscript indexing (zero-based and negative), slicing (list[start:stop:step]), in-place element updates, concatenation (+), repetition (*), and built-in methods (append, insert, pop, remove, extend, sort, reverse).

List Comprehensions Syntax & Pipeline

A List Comprehension provides a concise, mathematically expressive syntax for creating a new list by applying an expression to each item in an existing iterable, optionally filtering elements.

नेपालीमा बुझ्नुपर्ने: लुप लगाएर नयाँ लिस्ट बनाउँदा ३–४ लाइन लाग्ने कोडलाई पाइथनमा एउटै ब्र्याकेट [...] भित्र लेख्न सकिन्छ। यसको फर्मुला हो: [expression for item in iterable if condition]। यो सामान्य लुपभन्दा C-level मा चल्ने हुनाले धेरै छिटो हुन्छ।
DIAGRAM 1: LIST COMPREHENSION TRANSFORMATION PIPELINEExecution Flow
1. Iterable
for x in range(10)
2. Filter (Optional)
if x % 2 == 0
3. Expression
x ** 2 (Output map)
4. New List
[0, 4, 16, 36, 64]
💡 TU Exam Comparison: Avoids calling list.append() repeatedly inside bytecode loop, eliminating function lookup overhead.
# 1. Standard approach (4 lines):
squares = []
for x in range(1, 6):
    if x % 2 != 0: squares.append(x * x)

# 2. Idiomatic List Comprehension (1 line):
squares_comp = [x * x for x in range(1, 6) if x % 2 != 0]
print(squares_comp) # [1, 9, 25]

02. Dictionaries & Dictionary Comprehensions

A Dictionary is a mutable, key-value mapping structure. In Python 3.7+, dictionaries maintain insertion order. Keys must be hashable (immutable types like strings, numbers, tuples). Values can be any Python object.

Accessing, Updating & Deleting Keys

  • d[key]: Direct lookup. Raises KeyError if key is missing.
  • d.get(key, default): Safe lookup. Returns default (or None) if key is missing without raising an exception.
  • d[key] = val or d.update({k: v}): Adds or updates key-value pairs.
  • del d[key] or d.pop(key): Deletes specific key and returns associated value.
DIAGRAM 2: DICTIONARY HASH TABLE BUCKET MAPPINGO(1) Average Lookup
1. Immutable Key
"roll_no"
hash("roll_no")
2. Hash Function & Index
hash % table_size
Bucket Index #4
3. Bucket Payload
[Key, Value Pointer]
"roll_no" ➔ 105
💡 Why lists cannot be dict keys: Lists are mutable and lack a static __hash__() value.

Dictionary Comprehensions

Syntax: {key_expr : val_expr for item in iterable if condition}.

# Inverting a dictionary (swapping key & value)
student_grades = {"Aayush": "A", "Binita": "B+", "Chirag": "A"}

# Dict comprehension with filtering:
top_students = {k: v for k, v in student_grades.items() if v == "A"}
print(top_students) # {'Aayush': 'A', 'Chirag': 'A'}

03. File Handling & The `with` Context Manager

Python abstracts file input/output through file streams. The built-in open(filename, mode) function returns a file object.

Essential File Access Modes in TU Exams

  • 'r': Read mode (default). Raises FileNotFoundError if file does not exist.
  • 'w': Write mode. Overwrites existing contents or creates a new file.
  • 'a': Append mode. Appends new data to the end without truncating existing content.
  • 'r+': Read & Write mode (file pointer at beginning).
  • 'rb' / 'wb': Binary mode (used for images, audio, pickled objects).
नेपालीमा बुझ्नुपर्ने: `with` किन अनिवार्य छ?
परम्परागत f = open(...) गर्दा अन्त्यमा f.close() लेख्न बिर्सिएमा वा बिचमा कुनै Exception आएर कोड क्र्यास भएमा फाइल खुला नै रहन्छ (Memory leak & descriptor exhaustion)। तर with open(...) as f: गर्दा काम सकिएपछि वा बिचमै एरर आए पनि पाइथनले आफैँ फाइल बन्द गरिदिन्छ (Context Manager Protocol)।
DIAGRAM 3: FILE BUFFER LIFECYCLE & CONTEXT MANAGER PROTOCOLGuaranteed Resource Cleanup
1. __enter__()
OS opens file handle
Assigns to `f`
2. Execution Block
f.read() / f.write()
Buffered I/O stream
3. __exit__()
Flush stream buffer
Auto close file handle
💡 TU Exam Rule: Always use with open() as f: in laboratory and exam code to show professional coding standards.

04. Reading & Writing Text and Numbers to Files

Files natively process strings or bytes. Writing numeric values requires converting them to strings (via str() or f-strings). Reading numeric values requires explicit type casting (int(), float()).

Three Reading Methods Comparison

  • f.read(size): Reads the entire file (or up to size characters) into a single string. (High RAM usage for large files).
  • f.readline(): Reads one single line including the trailing \n. Returns empty string "" at EOF.
  • f.readlines(): Reads all lines and returns a list of strings (each line as an element).
  • for line in f:: Memory-efficient iterator that streams line-by-line using constant memory ($O(1)$ space).
# Writing numbers to file (Classic TU Question)
scores = [85, 92, 78, 90, 66]
with open("marks.txt", "w") as f:
    for score in scores:
        f.write(f"{score}\n") # Must be converted to string

# Reading numbers back and calculating average:
total, count = 0, 0
with open("marks.txt", "r") as f:
    for line in f:
        val = int(line.strip()) # Convert back to integer
        total += val; count += 1
print(f"Average Score: {total / count:.2f}")

05. File Pointer Positioning: `tell()` & `seek()`

The operating system maintains an internal byte cursor (file pointer) that tracks where the next read or write operation will take place.

  • f.tell(): Returns an integer indicating the current byte offset of the file pointer from the beginning.
  • f.seek(offset, whence): Repositions the file pointer.
    • whence = 0 (default): Relative to beginning of file (e.g. f.seek(0) rewinds to start).
    • whence = 1: Relative to current cursor position (binary mode).
    • whence = 2: Relative to end of file (e.g. f.seek(0, 2) jumps to EOF).
with open("data.txt", "w+") as f:
    f.write("TU MCA Python Guide")
    print("Position after write:", f.tell()) # 19 bytes
    f.seek(0) # Rewind pointer back to start!
    print("Read first 6 chars:", repr(f.read(6))) # 'TU MCA'

06. The `os` and `sys` Modules (File System & Runtime)

Python interacts with the host operating system through the os module (file system, directory operations, paths) and the sys module (interpreter runtime parameters, command-line arguments).

DIAGRAM 4: OS & SYS SYSTEM KERNEL INTERACTIONOS Interface Architecture
`os` Module (File System)
  • os.getcwd(): Current working dir
  • os.mkdir('folder'): Create folder
  • os.listdir('.'): Directory listing
  • os.path.join(a, b): Safe OS path stitching
  • os.remove(file): Delete file
`sys` Module (Interpreter State)
  • sys.argv: Command-line argument array
  • sys.exit([code]): Halt program immediately
  • sys.path: Module search path list
  • sys.version: Python runtime version
  • sys.stdin / stdout: Standard I/O streams
💡 Cross-Platform Tip: Never hardcode path slashes (e.g. C:\\folder\\file). Always use os.path.join('folder', 'file').

07. Formatted Files: CSV & Tab-Separated Values (TSV)

A CSV (Comma-Separated Values) file stores tabular data in plain text. Each line is a data record; fields within a line are separated by a delimiter (comma , for CSV or tab \t for TSV). Python includes a specialized csv module.

DIAGRAM 5: CSV DELIMITER PARSING & DICTREADER MAPPINGTabular Data Transformation
Raw Text on Disk (students.csv):
Roll,Name,Marks\n101,Aayush,88\n102,Binita,94
➔ csv.DictReader(file) ➔
Structured Python Dictionaries (Row-by-Row):
Row 1: {'Roll': '101', 'Name': 'Aayush', 'Marks': '88'}
Row 2: {'Roll': '102', 'Name': 'Binita', 'Marks': '94'}
💡 Windows Pitfall: When opening a file for writing with csv.writer, always add newline='' (e.g. open('data.csv', 'w', newline='')) to prevent blank rows.
# Complete TU Lab Model: Writing & Reading CSV
import csv

# 1. Writing to CSV:
data = [
    ["ID", "Course", "Credit"],
    ["MCA501", "Advanced OS", 3],
    ["MCA504", "Python Programming", 3]
]
with open("courses.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# 2. Reading via DictReader:
with open("courses.csv", "r") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"{row['ID']} : {row['Course']}")
Section 08 • High-Probability TU Examination Questions

TU Exam Q&A Bank: Unit 2 Model Answers

Structured in Quick-Grab Bullet Points for instant revision, followed by Paragraph Elaboration Blueprints for writing 1–2 page answers in the semester exam.

QUESTION 1 • 5 / 10 MARKS

What is a List Comprehension? Compare it with standard `for` loops in terms of syntax, readability, and execution speed.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • Definition: Compact syntactic construct to create new lists by transforming and filtering iterables.
  • General Syntax: [expression for item in iterable if condition].
  • Performance Advantage: Evaluated in internal C-level instructions, avoiding repeated bytecode function lookups like append().
  • Readability: Replaces 4-5 lines of loop initialization boilerplate with a single declarative line.
  • Overuse Caveat: Overly complex or deeply nested comprehensions should be avoided to maintain clarity.

परीक्षामा लामो उत्तर यसरी लेख्ने (Paragraph Elaboration Blueprint):

Core Definition: A list comprehension is a concise and elegant syntax for generating a new list out of an existing sequence or iterable. Borrowed from functional languages (Haskell/Set-builder notation in mathematics), it unifies element transformation and conditional filtering into a single expressive statement.

Execution Mechanics & Performance: When using a standard for loop, each iteration requires the PVM to perform an attribute lookup for list.append, allocate frame overhead, and invoke the method. In contrast, a list comprehension is compiled into specialized C-level bytecode (LIST_APPEND in CPython), which preallocates list buffers and appends values in native C speed without method lookup overhead.

# Model Exam Snippet
words = ["kathmandu", "pokhara", "butwal", "dharan"]
capitalized = [w.upper() for w in words if len(w) > 6]
print(capitalized) # ['KATHMANDU', 'POKHARA']
नेपालीमा सार: [expr for x in list if cond] ले ३-४ लाइनको लुपलाई एकै लाइनमा समेट्छ र सामान्य लुपभन्दा धेरै छिटो चल्छ।
QUESTION 2 • 5 MARKS

Explain how Python Dictionaries work internally. Why must dictionary keys be hashable? Compare `d[key]` with `d.get()`.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • Hash Table Implementation: Dictionaries use sparse hash tables providing average $O(1)$ constant time complexity for lookups, insertions, and deletions.
  • Hashability Requirement: A key must implement __hash__() and remain immutable across its lifetime (e.g. str, int, tuple).
  • Mutable Keys Prohibited: Lists and dictionaries cannot be keys because changing their contents alters their hash address.
  • Subscript `d[key]`: Raises KeyError if the key is not present.
  • Safe `d.get(key, default)`: Gracefully returns None or a fallback default value without crashing.

परीक्षामा लामो उत्तर यसरी लेख्ने (Paragraph Elaboration Blueprint):

Internal Architecture: Python dictionaries are implemented using open-addressing hash tables with quadratic probing for collision resolution. When a key is inserted, Python computes its hash integer via hash(key), maps it to a bucket index modulo the table size, and stores the key reference and value reference.

Exception Safety with `get()`: In real-world programs, accessing a missing key using d[key] halts the execution by raising a KeyError. Production code uses d.get(key, fallback), which allows defensive programming without wrapping every lookup in try...except.

info = {"faculty": "Humanities", "code": "MCA504"}
# print(info["credits"]) ➔ KeyError: 'credits'
credits = info.get("credits", 3) # Returns fallback: 3
print("Resolved credits:", credits)
नेपालीमा सार: डिक्सनरीमा डाटा खोज्दा d[key] ले एरर (KeyError) फाल्न सक्छ, तर d.get() ले एरर नफाली सुरक्षित रूपमा डिफल्ट मान दिन्छ।
QUESTION 3 • 5 / 10 MARKS

Explain the Context Management Protocol in Python File Handling. Why is `with open(...)` preferred over `open()` and `close()`?

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • Resource Leak Hazard: Forgetting f.close() causes operating system file descriptor leaks and un-flushed buffers.
  • Exception Vulnerability: If an exception occurs before reaching f.close(), the file remains open indefinitely.
  • Context Manager: The with statement triggers __enter__() and guarantees __exit__() invocation.
  • Deterministic Cleanup: Automatically flushes and closes the file stream under all circumstances (normal exit or crash).
  • Clean Readability: Reduces cumbersome try...finally boilerplate.

परीक्षामा लामो उत्तर यसरी लेख्ने (Paragraph Elaboration Blueprint):

The Problem with Traditional File Handling: In traditional file handling, a file descriptor is requested from the OS kernel via f = open("file.txt"). If an unhandled exception occurs (such as a ValueError during number parsing or an index crash), the subsequent f.close() call is bypassed. In server applications, running out of OS file handles causes fatal system crashes.

The `with` Statement Solution: Python introduces the Context Manager Protocol consisting of two dunder methods: __enter__() and __exit__(). When executing with open(...) as f:, Python executes __enter__() which acquires the resource. Regardless of how control flow exits the block (whether through normal completion, a return, a break, or an unexpected exception), Python executes __exit__(), ensuring the file stream is flushed and closed cleanly.

# Industry Standard Safe Pattern:
with open("sample.txt", "w") as f:
    f.write("TU MCA Tribhuvan University")
# File is 100% closed here automatically!
print("Is file closed?", f.closed) # True
नेपालीमा सार: with प्रयोग गर्दा कोडको बिचमै एरर आएर प्रोग्राम रोकिए पनि फाइल आफैँ सुरक्षित बन्द हुन्छ, त्यसैले f.close() लेखिरहनु पर्दैन।
QUESTION 4 • 5 MARKS

Explain the significance of `tell()` and `seek()` in Python file pointer manipulation with a complete program.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • File Pointer: An internal cursor indicating the current byte offset where reading or writing will occur.
  • `tell()` Method: Returns the current byte location of the file cursor as an integer.
  • `seek(offset, whence)`: Moves the file cursor to a new location.
  • Whence Values: 0 = from beginning (default), 1 = from current, 2 = from end of file.
  • Rewind Idiom: f.seek(0) resets the pointer back to the beginning of the file.

परीक्षामा लामो उत्तर यसरी लेख्ने (Paragraph Elaboration Blueprint):

File Cursor Mechanics: Every open file stream maintains an internal position pointer. After writing or reading $N$ bytes, the pointer advances by $N$ bytes. If you write data in w+ mode and immediately call read(), Python returns an empty string because the cursor is resting at the end of the file (EOF).

Repositioning with `seek()`: To re-read what was just written without closing and reopening the file, we call f.seek(0) to reset the cursor to offset zero. The whence argument controls the reference anchor point: 0 for file start, 1 for current position, and 2 for end-of-file.

with open("test.txt", "w+") as f:
    f.write("ABCDEFGHIJ")
    print("Cursor at:", f.tell()) # 10
    f.seek(3) # Move to index 3 (offset 3)
    print("Read from index 3:", repr(f.read(4))) # 'DEFG'
नेपालीमा सार: tell() ले फाइलमा हाल कर्सर कति बाइट पर छ भनेर देखाउँछ भने seek(0) ले कर्सरलाई सिधै फाइलको सुरुमा फर्काउँछ।
QUESTION 5 • 5 MARKS

Write a Python program to store a list of integers into a text file and read them back to compute their sum and average.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • String Conversion Required: write() only accepts strings; passing integers raises a TypeError.
  • Delimiter: Append a newline (\n) after each number so they can be parsed line-by-line.
  • Line Stripping: Use line.strip() to remove trailing newline characters before casting.
  • Type Casting Back: Cast extracted strings back to numeric format via int() or float().
  • Memory Stream: Use a simple for line in f: loop for memory-efficient iteration.
numbers = [15, 25, 35, 45, 55]

# Step 1: Write numbers as strings
with open("numbers.txt", "w") as f:
    for n in numbers:
        f.write(f"{n}\n")

# Step 2: Read back and calculate
total, count = 0, 0
with open("numbers.txt", "r") as f:
    for line in f:
        total += int(line.strip())
        count += 1

print(f"Total: {total}, Average: {total / count}") # Total: 175, Avg: 35.0
नेपालीमा सार: फाइलमा नम्बर लेख्दा सधैं str(num) वा f-string बनाएर लेख्नुपर्छ। पढ्दा line.strip() गरेर int() मा बदल्नुपर्छ।
QUESTION 6 • 5 / 10 MARKS

Explain how to manipulate CSV files in Python. Differentiate between `csv.reader` and `csv.DictReader`.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • CSV Structure: Delimited text files where commas separate fields and newlines separate records.
  • `csv.reader`: Parses rows into standard lists of strings accessed via numerical index (row[0], row[1]).
  • `csv.DictReader`: Uses the first line as column headers and maps subsequent rows into dictionaries (row['Name']).
  • Maintainability: DictReader is superior because adding or reordering columns does not break code.
  • `newline=''` Parameter: Required when opening files for CSV writing on Windows to prevent extra blank lines.

परीक्षामा लामो उत्तर यसरी लेख्ने (Paragraph Elaboration Blueprint):

Comparison: Both utilities belong to the standard csv module. When using csv.reader, data is returned as positional arrays of strings. If the schema of the CSV file changes (for example, inserting an "Email" column before "Age"), all positional lookups like row[2] will inadvertently fetch the wrong data.

Advantage of DictReader: csv.DictReader reads the initial header record automatically and binds each data field to its corresponding column key. This makes the code self-documenting and resilient against column order rearrangements.

import csv
with open("data.csv", "r") as f:
    reader = csv.DictReader(f)
    for row in reader:
        print(f"Student {row['Name']} scored {row['Marks']}")
नेपालीमा सार: csv.reader ले डाटालाई लिस्ट (इन्डेक्स ०, १) को रूपमा दिन्छ, तर csv.DictReader ले हेडिङ अनुसार डिक्सनरी (row['Name']) बनाएर दिन्छ, जुन निकै भरपर्दो हुन्छ।
QUESTION 7 • 5 MARKS

How does Python handle file system and directory manipulation using the `os` module? Give examples of five essential functions.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • `os.getcwd()`: Returns absolute path string of Current Working Directory.
  • `os.mkdir()` & `os.makedirs()`: Creates a single folder or recursive nested directory structures.
  • `os.listdir(path)`: Returns a list containing the names of files and directories in path.
  • `os.remove()` & `os.rmdir()`: Deletes an individual file or empty directory.
  • `os.path.join()`: Concatenates path components using OS-specific separators (/ or \).
import os
curr_dir = os.getcwd()
new_folder = os.path.join(curr_dir, "tu_assignments")
if not os.path.exists(new_folder):
    os.mkdir(new_folder)
print("Directory files:", os.listdir(curr_dir))
नेपालीमा सार: कम्प्युटरको फोल्डर बनाउन (mkdir), भित्रका फाइल हेर्न (listdir), वा पाथ मिलाउन (path.join) os मोड्युल प्रयोग गरिन्छ।
QUESTION 8 • 5 MARKS

Explain the role of the `sys` module in Python with special focus on `sys.argv` command-line argument processing.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • Runtime Interface: sys exposes variables and functions interacting with the Python interpreter runtime.
  • `sys.argv`: List of strings representing command-line arguments passed to script.
  • Index 0: sys.argv[0] is always the script's own file name.
  • Index 1 to N: Arguments passed after the script name (e.g. python app.py input.txt 100).
  • Argument Validation: Always check len(sys.argv) before indexing to prevent IndexError.
import sys
if len(sys.argv) < 2:
    print("Usage: python script.py <username>")
    sys.exit(1)
username = sys.argv[1]
print(f"Welcome to TU MCA, {username}!")
नेपालीमा सार: टर्मिनलबाट कमान्ड हान्दा दिइएका इनपुटहरू (e.g. python app.py Ram) पढ्न sys.argv लिस्ट प्रयोग गरिन्छ।
QUESTION 9 • 5 MARKS

Write a Python program using Nested List Comprehension to transpose a 2D matrix (rows to columns).

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • 2D Representation: A matrix is represented as a list of lists (e.g. [[1, 2], [3, 4]]).
  • Transpose Definition: Swapping row indices and column indices ($A[i][j] \➔ A^T[j][i]$).
  • Nested Comprehension Syntax: [[row[col_idx] for row in matrix] for col_idx in range(len(matrix[0]))].
  • Outer Loop: Iterates through column indices.
  • Inner Loop: Picks elements from each row corresponding to that column index.
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
transpose = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
print("Transposed Matrix:", transpose)
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
नेपालीमा सार: म्याट्रिक्सको Row लाई Column बनाउन नेस्टेड लिस्ट कम्प्रिहेन्सनले बाहिरी लुपमा कोलम र भित्री लुपमा रो का इलिमेन्ट निकालेर सजिलै Transpose निकाल्छ।
QUESTION 10 • 5 MARKS

Compare `read()`, `readline()`, and `readlines()` in terms of return type and memory efficiency for large datasets.

कण्ठ पार्ने ५ मुख्य बुँदाहरू (Quick-Grab Memory Points):
  • `read()`: Reads entire file content into one massive single string (Risks Out-of-Memory).
  • `readline()`: Reads exactly one single line as a string up to \n.
  • `readlines()`: Reads all lines at once and returns a list of strings.
  • Memory Winner: Direct iteration for line in f: utilizes a buffer iterator, consuming negligible RAM.
  • EOF Indicator: read() and readline() return empty string "" at EOF.
# Safest memory-friendly reading idiom for large files:
with open("huge_dataset.txt", "r") as f:
    for line in f: # Constant O(1) memory! Does not load full file.
        process(line.strip())
नेपालीमा सार: ठूला फाइलहरू पढ्दा read() वा readlines() ले पुरै फाइल एकैचोटि मेमोरीमा हाल्ने हुँदा कम्प्युटर ह्याङ हुन सक्छ। त्यसैले सधैं for line in f: लुप प्रयोग गर्नु उत्तम हुन्छ।