Programming fundamentals

Class 12 Computer Science, unit 3, book pages 59–99

This unit moves from writing small Python programs to organising larger ones. It compares the functional and object-oriented programming paradigms, then uses Python's data structures (lists, tuples, sets, dictionaries and nested lists), files, GUIs with Tkinter and Pygame, and SQLite databases to solve real problems. It ends with the techniques that make programs correct: debugging with print statements, breakpoints and watchpoints, and unit testing.

Learning outcomes (4)
  • Understand and evaluate applications of various programming paradigms.
  • Use more advanced programming constructs such as data structures (lists and so on), file handling (disk I/O to write to storage) and databases in Python.
  • Implement complex algorithms that use lists and other data structures in Python.
  • Use more advanced techniques (unit tests, breakpoints, watches) for testing and debugging code in Python.

3.1Programming paradigmOften examined

A programming paradigm is a style or method of writing and organising program code. It decides how the code is structured, how control flows from one statement to the next, and how data is managed. In other words, it is a particular methodology for using a programming language to solve a problem.

A programmer may create his own functions and structures inside a language, but these are not a paradigm. The paradigm comes from the language itself: the language gives guidelines, tools and features that tell the programmer what to build and how to build it. For example, Python supports both the functional and the object-oriented paradigms, while SQL follows the declarative (query) style.

Why paradigms are important

  • Complexity reduction: a paradigm breaks a big, complex problem into smaller parts (functions or objects) that are easier to understand and solve.
  • Structure: code, program flow and data are organised in a standard, predictable way, so the program is easier to read, test and maintain.
  • Standard tools: each paradigm comes with well-known techniques that many programmers already use, so a team can work together more easily.
  • Focus: the developer concentrates on the task in hand instead of reinventing a way to organise the program.
Common programming paradigms (the book studies the functional and object-oriented paradigms)
ParadigmMain ideaUnit of the programExample languages
Procedural (imperative)A sequence of steps that change variablesProcedure / block of statementsC, Pascal, Python
FunctionalSolve by calling functions; avoid changing stateFunctionHaskell, Lisp, Python
Object-orientedModel the problem as objects that hold data and behaviourClass and objectJava, C++, Python
Declarative (query)State what result is wanted, not howQuery / ruleSQL

3.1.1Functional programmingOften examined

Functional programming is a paradigm in which the program is divided into functions according to their functionality. The solution of the problem is broken into segments and each segment is handled by a function. Every step of the solution is mapped to a group of functions.

In functional programming the developer focuses on what is to be achieved rather than how to achieve it. Functions take inputs and return outputs and, ideally, do not change any value outside themselves (they have no side effects and do not change the program's state). This is why the MCQ phrase "uses functions without changing state" means functional programming.

Pure function
A function whose result depends only on its inputs and which does not change any variable outside itself. Calling it twice with the same input always gives the same output.
Higher-order function
A function that takes another function as an argument or returns a function, for example Python's and .
Lambda
A small nameless function written in one line, such as .
Table 3.1: Pros and cons of functional programming
ProsCons
Simpler code.Not easy for beginners; needs practice.
Code is easier to track for logic and to debug.May involve overhead, especially when handling large data structures.
Functions are easier to test and run.Complex algorithms may be harder to express, often requiring higher-order functions.
Naturally supports parallel and concurrent programming.Requires careful handling of data sharing and synchronisation for efficient execution.
Emphasises what to compute rather than how, hence more concise code.May be less efficient in performance-critical scenarios.

If the flow of the solution is clear, functional programming makes the work easier. Updating the code for any change is simple, because only the particular function that is affected needs to be modified; the whole program does not need to be read and understood again.

Functional style in Python
Use small pure functions with , and a lambda to process a list of marks.
# Functional style: small pure functions, no global state is changed
def square(n):
    return n * n

def is_even(n):
    return n % 2 == 0

marks = [3, 8, 5, 10, 6]

squares = list(map(square, marks))           # apply square() to every item
evens = list(filter(is_even, marks))          # keep only the even items
total = sum(map(lambda n: n * 2, marks))      # lambda = small nameless function

print("Original list:", marks)
print("Squares:", squares)
print("Even marks:", evens)
print("Sum of doubled marks:", total)
Functional style: data flows through functions
Original list: [3, 8, 5, 10, 6]
Squares: [9, 64, 25, 100, 36]
Even marks: [8, 10, 6]
Sum of doubled marks: 64
Output
  • Lines 2 to 6 define two pure functions. Each one only returns a value; neither changes the list.
  • Line 10: applies to every element; collects the results.
  • Line 11: keeps only the elements for which returns True.
  • Line 12: a lambda doubles each mark and adds them: .
  • The original list is unchanged at the end, which is the functional idea of not changing state.

For comparison, the same total written in the ordinary step-by-step (imperative) style changes the variable in every pass of the loop:

# Imperative (procedural) style: a variable is changed step by step
marks = [3, 8, 5, 10, 6]
total = 0
for m in marks:
    total = total + m * 2      # state (total) changes in every pass
print("Sum of doubled marks:", total)
Imperative style: state changes inside a loop
Sum of doubled marks: 64
Output

3.1.2Object oriented programmingOften examined

Object oriented programming (OOP) is a software development paradigm based on data and objects. An object contains attributes (data that describe its properties) and methods (functions that define its behaviour). Programs are built around objects that copy real-world things, such as a car, a fruit or a bank account, so developers understand the problem more naturally.

The main advantage of OOP is that the emphasis in problem solving is on objects and not on the logic. OOP is implemented through classes: first a class defines the object and its functions, and later actual instances (objects) are created when they are needed in the program. OOP limits direct access to data, which gives safety and reliability of data and programs. It is very useful for complex systems in which developers map real objects and their relationships.

Table 3.2: Pros and cons of object oriented programming
ProsCons
Simplifies complex systems by breaking them into smaller reusable components.May result in complex designs if not implemented thoughtfully.
Hides implementation details, hence improving code maintainability and security.At times code is harder to understand and debug due to encapsulation.
Establishes hierarchical relationships between classes.Rigid design due to inheritance may reduce flexibility.
Objects of different types are treated as if they were of the same type.Polymorphism increases complexity, making code harder to understand and reason about.
Represents real-world objects and their relationships effectively.May not be well-suited for certain problems, such as computationally intensive tasks.

Key concepts: class and object

Class
An object creation stencil or template. It specifies the attributes (variables or properties) and methods (functions) that its objects will hold.
Object
An instance of a class that denotes a real thing, such as a person, a car or a bank account. Every object has its own attribute values but uses the methods defined in the class.
Instance
Creating an object from a class is called instantiation; the object is an instance of that class.

A class is like the map of a house; each house (object) built from that map has particular properties such as size, colour or number of rooms. All objects share the basic structure defined by the class but have their own values. In the book's example the class is the stencil for every stationery item in a bookshop:

One class, two objects with different attribute values
Class: StationaryItemObject 1: a blue XYZ penObject 2: a red ABC pencil
name"Pen""Pencil"
price200100
color"Blue""Red"
brand"XYZ""ABC"
methods, same methods
The StationaryItem class in Python
Write the class with a constructor and the methods get_price() and set_price(), then create the two objects of the table.
class StationaryItem:
    def __init__(self, name, price, color, brand):   # constructor: runs when an object is made
        self.name = name          # attributes (data)
        self.price = price
        self.color = color
        self.brand = brand

    def get_price(self):          # method (behaviour)
        return self.price

    def set_price(self, new_price):
        self.price = new_price

pen = StationaryItem("Pen", 200, "Blue", "XYZ")        # object 1
pencil = StationaryItem("Pencil", 100, "Red", "ABC")   # object 2

print(pen.name, pen.color, pen.get_price())
print(pencil.name, pencil.color, pencil.get_price())
pencil.set_price(120)
print("New price of pencil:", pencil.get_price())
Class, objects, attributes and methods
Pen Blue 200
Pencil Red 100
New price of pencil: 120
Output
  • Line 1: starts the definition of the template.
  • Line 2: is the constructor. It runs automatically when an object is created and stores the four attribute values. means "this object".
  • Lines 8 to 12: two methods read and change the price, so other code does not touch the attribute directly.
  • Lines 14 and 15 create two objects (instances) from the same class.
  • Line 19 changes only the pencil's price; the pen is not affected because each object keeps its own data.

Encapsulation

Encapsulation is information hiding: the data (attributes) and the methods that operate on the data are combined into a single unit, the class. Direct access to some components is not allowed; they are reached only through designated methods. The internal working of the object stays hidden. Encapsulation protects data from unwanted access or changes and makes code modular and easier to read.

In a bank, the holder's name, account number and balance are critical. Instead of changing the balance directly, adds to it, subtracts from it and reads it without changing it.

Encapsulation: a bank account
Hide the balance so that it can be changed only through methods.
class BankAccount:
    def __init__(self, holder, balance):
        self.holder = holder
        self.__balance = balance          # two underscores: hidden (private) attribute

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount

    def withdraw(self, amount):
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient balance")

    def get_balance(self):
        return self.__balance

acc = BankAccount("Ayesha", 5000)
acc.deposit(1500)
acc.withdraw(10000)
acc.withdraw(2000)
print("Balance:", acc.get_balance())
try:
    print(acc.__balance)                  # direct access is blocked
except AttributeError:
    print("Direct access to __balance is not allowed")
Encapsulation with a private attribute
Insufficient balance
Balance: 4500
Direct access to __balance is not allowed
Output

The name (two leading underscores) makes the attribute private: code outside the class cannot read it as . The withdrawal of 10000 is refused by the method's check, which protects the data. Final balance: .

Abstraction

Abstraction shows only the necessary information and hides the background details or implementation. A driver uses start, stop and accelerate, but how the engine burns fuel and how gears shift are hidden. In code, the user of a class only needs to know the names of its methods, not how they are written.

Inheritance

Inheritance lets you create new classes (subclasses or derived classes) from existing parent classes. A subclass inherits the attributes and methods of its parent, which reduces repetition of code and extends existing structures efficiently. A has speed, colour, wheels and the operations and . A is a type of vehicle, so it inherits all these and adds its own, such as and .

Inheritance: Vehicle and Car
Create Car as a child class of Vehicle.
class Vehicle:                          # parent (base) class
    def __init__(self, color, wheels):
        self.color = color
        self.wheels = wheels
        self.speed = 0

    def accelerate(self, amount):
        self.speed += amount

    def brake(self):
        self.speed = 0

class Car(Vehicle):                     # child (derived) class inherits Vehicle
    def __init__(self, color, seating_capacity):
        super().__init__(color, 4)      # reuse the parent constructor
        self.seating_capacity = seating_capacity

    def open_sunroof(self):             # extra behaviour only a Car has
        return "Sunroof opened"

c = Car("White", 5)
c.accelerate(60)                        # inherited method
print("Colour:", c.color, "| Wheels:", c.wheels, "| Speed:", c.speed)
print("Seats:", c.seating_capacity, "|", c.open_sunroof())
c.brake()
print("Speed after brake:", c.speed)
Car reuses everything in Vehicle
Colour: White | Wheels: 4 | Speed: 60
Seats: 5 | Sunroof opened
Speed after brake: 0
Output

Writing makes Car a child of Vehicle. calls the parent constructor, so colour, wheels and speed are set without rewriting code. and are used by the Car object although they are written only in Vehicle.

Polymorphism

Polymorphism ("many forms") allows objects of different classes to be treated as instances of a common type, although each class implements the common operation in its own way. It allows generic code that works with many kinds of objects. A triangle, a rectangle and a circle are all shapes, but the area of a triangle needs base and height, a rectangle needs length and width, and a circle needs the radius. Instead of three differently named methods, each class has an method and the same instruction gives the correct result for each object.

Polymorphism: one method name, many shapes
Call area() on a triangle, a rectangle and a circle in one loop.
import math

class Shape:
    def area(self):
        return 0

class Triangle(Shape):
    def __init__(self, base, height):
        self.base, self.height = base, height
    def area(self):                       # same method name, own formula
        return 0.5 * self.base * self.height

class Rectangle(Shape):
    def __init__(self, length, width):
        self.length, self.width = length, width
    def area(self):
        return self.length * self.width

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    def area(self):
        return round(math.pi * self.radius ** 2, 2)

shapes = [Triangle(6, 4), Rectangle(5, 3), Circle(2)]
for s in shapes:                          # one instruction, three behaviours
    print(type(s).__name__, "area =", s.area())
Polymorphism through method overriding
Triangle area = 12.0
Rectangle area = 15
Circle area = 12.57
Output

The loop does not check the type of . Python calls the that belongs to each object's own class: , and .

3.2Python building blocks (revision)

Unit 3 uses Python constructs from Grade 11 in every program. This short revision collects them so that the programs in the rest of the unit are easy to follow.

Data types and operators

Built-in data types used in this unit
TypeExampleMutable?
intno
floatno
strno
boolno
listyes
tupleno
setyes
dictyes
Python operators
Operator groupOperatorsExample and result
Arithmetic gives 3, gives 1
Relational gives True
Logicaland, or, not gives False
Assignment adds 1 to x
Membershipin, not in gives True
age = 17                 # int: whole number
height = 5.6             # float: number with a decimal point
name = "Hina"            # str: text
passed = True            # bool: True or False
print(type(age), type(height), type(name), type(passed))
print(7 / 2, 7 // 2, 7 % 2, 2 ** 5)       # division, floor division, remainder, power
print(10 > 3 and 2 > 5, not passed)       # logical operators
print("Py" + "thon", "ab" * 3)            # string joining and repetition
Data types and operators
<class 'int'> <class 'float'> <class 'str'> <class 'bool'>
3.5 3 1 32
False False
Python ababab
Output

Control structures

Selection (, , ) chooses one path. Repetition (, ) repeats statements. leaves a loop and skips to the next pass. Python marks a block by indentation (4 spaces), not by braces.

marks = [45, 78, 91, 33]
for m in marks:                     # for loop: repeats once for each item
    if m >= 80:                     # if-elif-else: choose one path
        grade = "A"
    elif m >= 50:
        grade = "B"
    elif m >= 40:
        grade = "C"
    else:
        grade = "Fail"
    print(m, grade)

n = 1
while n <= 16:                      # while loop: repeats while the condition is True
    print(n, end=" ")
    n = n * 2
print()
for i in range(1, 10):
    if i == 4:
        continue                    # skip the rest of this pass
    if i == 7:
        break                       # leave the loop completely
    print(i, end=" ")
Selection and loops
45 C
78 B
91 A
33 Fail
1 2 4 8 16 
1 2 3 5 6 
Output

Functions

A function is a named block of code that does one job. It is defined with , receives values through parameters, and sends a result back with . Functions let us reuse code and split a big problem into small parts, which is the basis of complexity reduction.

def area_of_rectangle(length, width=1):     # width has a default value
    """Return the area of a rectangle."""
    return length * width

def describe(n):
    if n % 2 == 0:
        return "even"
    return "odd"

a = area_of_rectangle(4, 5)     # arguments 4 and 5 go to length and width
b = area_of_rectangle(7)        # width uses its default value 1
print(a, b)
print(9, "is", describe(9))
Defining and calling functions
20 7
9 is odd
Output

Strings

A string is a sequence of characters. Characters are numbered from index 0; a negative index counts from the end. Strings are immutable, so methods such as return a new string.

s = "Programming Fundamentals"
print(len(s))               # number of characters
print(s[0], s[-1])          # first and last character (index starts at 0)
print(s[0:11])              # slicing: characters 0 to 10
print(s.upper())
print(s.lower().count("a"))
print(s.split())            # break into a list of words
print(s.replace("Fundamentals", "Basics"))
print("gram" in s)
Common string operations
24
P s
Programming
PROGRAMMING FUNDAMENTALS
3
['Programming', 'Fundamentals']
Programming Basics
True
Output

3.2.1ListOften examined

A list is a basic data structure in Python that holds a group of elements in a sequence. It is written in square brackets, for example . Lists are flexible: one list may hold elements of different data types (integers, strings, booleans and so on).

  • Ordered: the order in which items are added is kept. Index 0 holds the first item, index 1 the second, and so on. This lets us retrieve an item by its position.
  • Mutable and dynamic: items can be added, changed or removed at any time, so the size of a list changes as needed.
  • Duplicates allowed: the same value may appear more than once.
Common list operations
Function / methodWhat it doesExample
puts x at index i and shifts later items right
adds x at the end
deletes the first item equal to x (error if x is absent)
deletes and returns the item at index i (last item if i is omitted)
, number of items, total of the items
True if x is in the list
Fig. 3.2: add and remove items from a list
A list stores 5 grades of a student. Insert, append, remove and pop grades, then find the average without a loop.
student_grades = [85, 92, 78, 95, 82]

student_grades.insert(2, 90)
print("Grades after adding a new grade:", student_grades)
student_grades.append(100)
print("Grades after adding another new grade:", student_grades)

student_grades.remove(78)
print("Grades after removing a grade:", student_grades)
lowest_grade = student_grades.pop(2)
print("Grades after removing the grade on index 2:", student_grades)

if 88 in student_grades:
    student_grades.remove(88)
else:
    print("Grade 88 not found in the list")

average_grade = sum(student_grades) / len(student_grades)
print("Average grade:", {average_grade})
Fig. 3.2 (numbered from line 1)
Grades after adding a new grade: [85, 92, 90, 78, 95, 82]
Grades after adding another new grade: [85, 92, 90, 78, 95, 82, 100]
Grades after removing a grade: [85, 92, 90, 95, 82, 100]
Grades after removing the grade on index 2: [85, 92, 95, 82, 100]
Grade 88 not found in the list
Average grade: {90.8}
Output
Line-by-line explanation
  • Line 1 creates the list with 5 elements.
  • Line 3: places 90 at index 2; 78, 95 and 82 move one place right.
  • Line 5: adds 100 at the end.
  • Line 8: deletes the value 78 (we know the value).
  • Line 10: deletes the element at index 2, which is now 90 (we know the position). The removed value is kept in .
  • Lines 13 to 16: 88 is removed only if it is in the list. Without this if-else, would stop the program with a ValueError.
  • Line 18: and give the average without writing a loop.
Activity 1: remove all odd numbers
Extend the program of Fig. 3.2 to search for all odd numbers in the list and remove them.
student_grades = [85, 92, 78, 95, 82]
student_grades.insert(2, 90)
student_grades.append(100)
print("List before:", student_grades)

odd_numbers = [g for g in student_grades if g % 2 != 0]   # find the odd items
print("Odd numbers found:", odd_numbers)
for g in odd_numbers:
    student_grades.remove(g)                              # remove each one
print("List after removing odd numbers:", student_grades)
List before: [85, 92, 90, 78, 95, 82, 100]
Odd numbers found: [85, 95]
List after removing odd numbers: [92, 90, 78, 82, 100]
Output

The odd numbers are first collected in a separate list (a value is odd when ). They are then removed one by one. Removing items from a list while looping over the same list skips elements, so the separate list is the safe method.

3.2.2Tuples

A tuple is written with round brackets, for example . It is a special type of list with one feature: it is immutable, so its contents cannot be updated once it is created. We can read the items and their arrangement but cannot add, remove or rearrange them. Tuples are used when a set of values must stay in order and unchanged throughout the program, for example the days of the week or geographical coordinates (latitude, longitude).

Fig. 3.3: tuples
Create a tuple of grades, access items by index, test membership, count a value and join two tuples.
grades = (85, 92, 78, 92, 85, 95)
print(grades)
print("First grade:", grades[0])
print("Third grade:", grades[2])
if 92 in grades:
    print("Student got 92")
print(grades.count(92))
new_grades = grades + (88, 90)
print(new_grades)
Fig. 3.3
(85, 92, 78, 92, 85, 95)
First grade: 85
Third grade: 78
Student got 92
2
(85, 92, 78, 92, 85, 95, 88, 90)
Output
  • Line 1 creates the tuple; line 2 prints it, showing that duplicates (85 and 92) are allowed.
  • Lines 3 and 4 access items by index (index starts at 0), so is the third grade, 78.
  • Line 5 uses the operator to check whether 92 is present.
  • Line 7: returns how many times 92 appears (2).
  • Line 8: the operator joins two tuples into a new tuple; the original is not changed.

Trying to change a tuple stops the program with an error:

days = ("Monday", "Tuesday", "Wednesday")
days[0] = "Sunday"
Traceback (most recent call last):
  File "prog.py", line 2, in <module>
    days[0] = "Sunday"
    ~~~~^^^
TypeError: 'tuple' object does not support item assignment
Output
List versus tuple
ListTuple
Square brackets [ ]Round brackets ( )
Mutable: items can be added, changed, removedImmutable: cannot be changed after creation
Methods such as append(), insert(), remove(), pop()Only count() and index()
Used for data that changesUsed for fixed data (days, coordinates)

3.2.3Sets

A set is written with curly brackets, for example . It is a collection of unique and unordered elements: a set cannot hold duplicate values, and if the same value is added twice only one copy is kept. Because it is unordered, a set has no index. Sets are used to store unique usernames, product IDs and so on, and the operator checks very quickly whether an element is present.

Fig. 3.4: sets
Create a set of grades containing duplicates, add a grade and check membership.
grades = {85, 92, 78, 92, 85, 95}
print(grades)
grades.add(88)
print(grades)
if 92 in grades:
    print("Student has 92 among the grades.")
Fig. 3.4
{92, 85, 78, 95}
{78, 85, 88, 92, 95}
Student has 92 among the grades.
Output
  • Line 1: the duplicates 85 and 92 are removed automatically, so the set keeps 4 values.
  • Line 3: inserts a new element.
  • Line 5: checks whether 92 exists and line 6 prints the message.

Sets also support the mathematical operations union, intersection and difference:

a = {1, 2, 3, 4}
b = {3, 4, 5}
print(a | b)        # union: items in either set
print(a & b)        # intersection: items in both sets
print(a - b)        # difference: items in a but not in b
a.discard(1)        # remove an item (no error if missing)
print(a, len(a))
Set operations
{1, 2, 3, 4, 5}
{3, 4}
{1, 2}
{2, 3, 4} 3
Output

3.2.4DictionaryOften examined

A dictionary stores data as key-value pairs, written as . Each key is linked with one value, just like a phone book where the person's name (key) is linked with the phone number (value). A list is searched by index, while a dictionary is searched by its key.

  • Keys must be unique and unchangeable (immutable) types such as strings, integers or tuples. A duplicate key is not created; assigning to an existing key updates its value.
  • Values can be any type: text, numbers, lists and even other dictionaries.
  • Dictionaries keep the link between keys and values (for example usernames and passwords) and, in Python 3.7 and later, remember the order in which keys were inserted.
Dictionary operations
OperationSyntax
Create
Read a value
Add or update
Delete
Check a key
Loop over pairs
Fig. 3.5: dictionary of student grades
Create a dictionary of three students, add Zaid, update Bilal, delete Dilawar and confirm the deletion.
student_grades = {
    'Amir': 90,
    'Bilal': 85,
    'Dilawar': 92
}

for student, grade in student_grades.items():
    print(f"{student}: {grade}")

student_grades['Zaid'] = 88
student_grades['Bilal'] = 87

del student_grades['Dilawar']

if 'Dilawar' in student_grades:
    print(f"\n Dilawar's grade:", student_grades['Dilawar'], "\n")
else:
    print("Student Dilawar not found \n")

for student, grade in student_grades.items():
    print(f"{student}: {grade}")
Fig. 3.5 (line 13 deletes Dilawar)
Amir: 90
Bilal: 85
Dilawar: 92
Student Dilawar not found 

Amir: 90
Bilal: 87
Zaid: 88
Output with line 13
  • Lines 1 to 5 create the dictionary: names are keys and marks are values.
  • Lines 7 and 8: gives each key-value pair, which the loop prints.
  • Line 10 adds a new key 'Zaid' at the end. Line 11 uses an existing key 'Bilal', so its value is updated from 85 to 87, not duplicated.
  • Line 13: removes the key 'Dilawar' and its value.
  • Lines 15 to 18 check whether 'Dilawar' is still a key; lines 20 and 21 print the final dictionary for confirmation.

If line 13 is commented out (), Dilawar is not deleted:

Amir: 90
Bilal: 85
Dilawar: 92

 Dilawar's grade: 92 

Amir: 90
Bilal: 87
Dilawar: 92
Zaid: 88
Output without line 13
Fig. 3.6: search an entry in a dictionary
Start with an empty dictionary, take student names and test numbers from the user until 'q' is entered, then search for a student.
student_test_number = {}
while True:
    name = input("Enter student name (or 'q' to quit): ")
    if name.lower() == 'q':
        break
    grade = float(input("Enter Test-Number for {}: ".format(name)))
    student_test_number[name] = grade

name = input("Enter student name to view test-number: ")
if name in student_test_number:
    print(f"{name}'s test-number: {student_test_number[name]}")
else:
    print(f"Student '{name}' not found.")
Fig. 3.6
Enter student name (or 'q' to quit): Amir
Enter Test-Number for Amir: 90
Enter student name (or 'q' to quit): Bilal
Enter Test-Number for Bilal: 87
Enter student name (or 'q' to quit): Dilawar
Enter Test-Number for Dilawar: 92
Enter student name (or 'q' to quit): Zaid
Enter Test-Number for Zaid: 88
Enter student name (or 'q' to quit): q
Enter student name to view test-number: Bilal
Bilal's test-number: 87.0
Sample run (user input shown after each prompt)
  • Line 1 creates an empty dictionary. Line 2 starts an endless loop that stops only at .
  • Line 4: accepts both 'q' and 'Q' to quit.
  • Line 6 converts the typed test number to float (so 87 is stored as 87.0) and line 7 stores it under the student's name.
  • Lines 9 to 13 search the dictionary with and print the value or a 'not found' message.

Finding a value in a dictionary versus a list

Searching a dictionary takes much less time than searching a list. To find a value in a list, Python checks the items one by one (linear search), so the time grows with the size of the list. A dictionary uses the key to jump directly to the value (hashing), so the time stays almost the same however large it is. The book tested this with 1 lakh (100 000) random 4-character entries; the program below repeats the experiment.

import random, string, time

# 1 lakh (100000) random 4-letter strings in a list, and the same strings as dictionary keys
words = [''.join(random.choices(string.ascii_lowercase, k=4)) for i in range(100000)]
table = {w: n for n, w in enumerate(words)}
target = words[-1]                      # worst case for the list: the last item

start = time.perf_counter()
found_in_list = target in words         # list: checks items one by one
list_time = (time.perf_counter() - start) * 1000

start = time.perf_counter()
found_in_dict = target in table         # dictionary: jumps straight to the key
dict_time = (time.perf_counter() - start) * 1000

print("Searching for:", target, found_in_list, found_in_dict)
print(f"List search time: {list_time:.4f} msec")
print(f"Dictionary search time: {dict_time:.4f} msec")
print("Dictionary is faster:", dict_time < list_time)
Timing a search in a list and in a dictionary
Searching for: nyss True True
List search time: 0.7784 msec
Dictionary search time: 0.0014 msec
Dictionary is faster: True
One run (the times change from run to run and from computer to computer)
Activity 2: add a missing student
Extend Fig. 3.5 so that if the searched name is not found, the user is asked whether to add it; on 'y' the key and its value are taken as input and added.
student_grades = {'Amir': 90, 'Bilal': 85, 'Dilawar': 92}

name = input("Enter student name to search: ")
if name in student_grades:
    print(f"{name}'s grade: {student_grades[name]}")
else:
    choice = input(f"{name} not found. Add this student? (y/n): ")
    if choice.lower() == 'y':
        grade = int(input(f"Enter grade for {name}: "))
        student_grades[name] = grade
        print(f"{name} added.")

for student, grade in student_grades.items():
    print(f"{student}: {grade}")
Enter student name to search: Sana
Sana not found. Add this student? (y/n): y
Enter grade for Sana: 77
Sana added.
Amir: 90
Bilal: 85
Dilawar: 92
Sana: 77
Sample run

3.2.5Nested listOften examined

A nested list is a list of lists: its elements (inner lists or sub-lists) are themselves lists, which may contain further lists. Nested lists are very useful for complex data such as matrices, where each inner list holds one row. The element in row and column is reached with two indexes: .

Fig. 3.8: printing a matrix using a nested list
Create a matrix and print it in three ways.
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
print(matrix)

for i in matrix:
    print(i)

for row in range(len(matrix)):
    for col in range(len(matrix)):
        print(matrix[row][col], end=', ')
Fig. 3.8
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
1, 2, 3, 4, 5, 6, 7, 8, 9, 
Output
  • Lines 1 to 5 define the matrix; each inner list is one row.
  • Line 6 prints the whole nested list in one line.
  • Lines 8 and 9: the loop runs 3 times (once per inner list) and prints one row per line, so the output looks like a matrix.
  • Lines 11 to 13: a nested loop. The outer index selects a row and the inner index visits every column of that row. keeps all 9 numbers on one line.

To print the elements in matrix form with the nested loop (the book leaves this as an activity), print a new line after each row:

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in range(len(matrix)):
    for col in range(len(matrix[row])):
        print(matrix[row][col], end="  ")
    print()                  # new line after each row
Nested loop printing in matrix form
1  2  3  
4  5  6  
7  8  9  
Output

Transpose of a matrix

The transpose of a matrix swaps rows and columns: the element at row , column moves to row , column . So in the program the indexes of the transpose are reversed: .

Fig. 3.9: program to create the transpose of a matrix
Find and print the transpose of a matrix.
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
print('Matrix is: ', matrix)
for i in matrix:
    print(i)

trans = [
    [0, 0, 0],
    [0, 0, 0],
    [0, 0, 0]
]

for row in range(len(matrix)):
    for col in range(len(matrix)):
        trans[col][row] = matrix[row][col]

print('Transpose of the Matrix is: ', trans)
for j in trans:
    print(j)
Fig. 3.9
Matrix is:  [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
Transpose of the Matrix is:  [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
[1, 4, 7]
[2, 5, 8]
[3, 6, 9]
Output
  • Lines 10 to 14 create , a matrix of zeros to hold the result.
  • Lines 16 and 17: the nested loop visits every element of .
  • Line 18 copies each element to the swapped position, so row 1 of the matrix becomes column 1 of the transpose.
  • Lines 20 to 22 print the result.

Be careful with the indexes. If they are not swapped, the program only copies the matrix:

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
trans = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
for row in range(len(matrix)):
    for col in range(len(matrix)):
        trans[row][col] = matrix[row][col]     # indices NOT swapped: a copy, not a transpose
print('Transpose of the Matrix is: ', trans)
Transpose of the Matrix is:  [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Output: the original matrix, not the transpose
Trace table of the nested loop in Fig. 3.9
rowcolmatrix[row][col]stored intrans after step
001trans[0][0][[1,0,0],[0,0,0],[0,0,0]]
012trans[1][0][[1,0,0],[2,0,0],[0,0,0]]
023trans[2][0][[1,0,0],[2,0,0],[3,0,0]]
104trans[0][1][[1,4,0],[2,0,0],[3,0,0]]
115trans[1][1][[1,4,0],[2,5,0],[3,0,0]]
126trans[2][1][[1,4,0],[2,5,0],[3,6,0]]
207trans[0][2][[1,4,7],[2,5,0],[3,6,0]]
218trans[1][2][[1,4,7],[2,5,8],[3,6,0]]
229trans[2][2][[1,4,7],[2,5,8],[3,6,9]]
Activity 3: change odd and even values
Tailor the code of Fig. 3.8 so that for all odd values of the matrix the sign is inverted, while all even values are multiplied by 2.
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]
for row in range(len(matrix)):
    for col in range(len(matrix[row])):
        if matrix[row][col] % 2 != 0:          # odd value: invert the sign
            matrix[row][col] = -matrix[row][col]
        else:                                  # even value: multiply by 2
            matrix[row][col] = matrix[row][col] * 2

for r in matrix:
    print(r)
[-1, 4, -3]
[8, -5, 12]
[-7, 16, -9]
Output
Activity 4: symmetric matrix
A matrix X is symmetric if X equals the transpose of X. Write code to determine whether a given matrix is symmetric.
def transpose(m):
    n = len(m)
    t = [[0] * n for i in range(n)]
    for row in range(n):
        for col in range(n):
            t[col][row] = m[row][col]
    return t

def is_symmetric(m):
    return m == transpose(m)          # symmetric if X equals its transpose

X = [[1, 7, 3],
     [7, 4, 5],
     [3, 5, 6]]
Y = [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]
print("X is symmetric:", is_symmetric(X))
print("Y is symmetric:", is_symmetric(Y))
X is symmetric: True
Y is symmetric: False
Output

Two nested lists are equal () when all their elements are equal in the same positions, so comparing X with its transpose answers the question directly.

3.2.6List as a value in dictionary

A dictionary holds one value per key, but that value can be a list, so several values can be stored under a single key. For example, each student's record can be stored with the student's name as the key and a list holding the registration number and marks as the value.

Fig. 3.10: a dictionary with a list
Store each student's registration number and three subject marks in a list under the student's name; read, insert, update and print the records.
students = {
    "Sara": ["345", 85, 90, 78],
    "Jamal": ["678", 75, 88, 92],
    "Zaid": ["912", 80, 79, 95]
}

sara_reg_num = students["Sara"][0]
print("Sara's Reg. No.: ", sara_reg_num)
jamal_marks = students["Jamal"][1]
print("Jamal's Marks for first subject are: ", jamal_marks)

students["Azra"] = ["234", 78, 85, 90]
print(students, "\n")

student_name = "Azra"
subject_index = 2
new_mark = 100
if student_name in students:
    students[student_name][subject_index] = new_mark
    print(f"*** Marks updated for {student_name} at index {subject_index} \n")
else:
    print("Student not found \n")

for name, details in students.items():
    print(f"Name: {name}")
    print(f"Registration Number: ", {details[0]})
    print(f"Marks: [", end='')
    print(details[1], ',', details[2], ',', details[3], "]\n")
Fig. 3.10
Sara's Reg. No.:  345
Jamal's Marks for first subject are:  75
{'Sara': ['345', 85, 90, 78], 'Jamal': ['678', 75, 88, 92], 'Zaid': ['912', 80, 79, 95], 'Azra': ['234', 78, 85, 90]} 

*** Marks updated for Azra at index 2 

Name: Sara
Registration Number:  {'345'}
Marks: [85 , 90 , 78 ]

Name: Jamal
Registration Number:  {'678'}
Marks: [75 , 88 , 92 ]

Name: Zaid
Registration Number:  {'912'}
Marks: [80 , 79 , 95 ]

Name: Azra
Registration Number:  {'234'}
Marks: [78 , 100 , 90 ]
Output
  • Lines 1 to 5: each value is a list; index 0 is the registration number and indexes 1 to 3 are marks.
  • Line 7 reads Sara's registration number with two indexes: the key, then the list index .
  • Line 9 selects Jamal's first-subject marks with .
  • Line 12 inserts a new record for Azra and line 13 prints the whole dictionary.
  • Lines 15 to 22 update Azra's second-subject marks: , but only if the key exists.
  • Lines 24 to 28 loop over the key-value pairs and print every record.
Activity 5: teachers of each class
A primary school has subject teachers such that a teacher teaches only 2 subjects to a class. Each class studies 8 subjects. Write a program using a list as a value in a dictionary, so that the class is the key and its teachers are stored as the value.
# Each class studies 8 subjects and each teacher teaches 2 of them,
# so every class has 8 / 2 = 4 teachers stored in a list.
school = {
    "Class 1": ["Ms. Amna", "Mr. Bilal", "Ms. Sadia", "Mr. Kamran"],
    "Class 2": ["Ms. Hina", "Mr. Tariq", "Ms. Nida", "Mr. Usman"],
}
school["Class 3"] = ["Ms. Rabia", "Mr. Faisal", "Ms. Sana", "Mr. Imran"]   # add a class
school["Class 2"][1] = "Mr. Waqas"                                       # replace one teacher

for cls, teachers in school.items():
    print(cls, "->", ", ".join(teachers), f"({len(teachers)} teachers)")
Class 1 -> Ms. Amna, Mr. Bilal, Ms. Sadia, Mr. Kamran (4 teachers)
Class 2 -> Ms. Hina, Mr. Waqas, Ms. Nida, Mr. Usman (4 teachers)
Class 3 -> Ms. Rabia, Mr. Faisal, Ms. Sana, Mr. Imran (4 teachers)
Output

3.2.7Files in PythonOften examined

Importance of disk I/O

Disk input/output (disk I/O) means reading data from, and writing data to, a storage device such as a hard disk (HDD) or SSD. Disk I/O is the bridge between the CPU and the disk: relevant data is passed from the disk to the CPU for processing and is later stored back on permanent storage. Almost every computer operation depends on this continuous exchange: launching programs, opening files and saving the changes made by the user. Fast, efficient disk I/O is therefore vital for good system performance.

Variables live in RAM and are lost when a program ends. A file stores data permanently on the disk, so a program can save results and read them again later. Reading a file from the hard disk is an example of disk I/O; storing a value in a variable is not.

Types of files

Text files versus binary files
Text fileBinary file
Stores data as readable characters.Stores data as a combination of 0s and 1s (binary data).
Each line ends with a special End Of Line (EOL) marker, usually , so Python knows a new line starts.No line structure or EOL marker; data is a stream of bytes.
Can be opened and read by humans in any text editor.Not meant for humans; read by other programs.
Examples: .txt, .csvExamples: images (.jpg, .png), audio (.mp3), video (.mp4), programs (.exe)
Opened with modes "r", "w", "a"Opened with modes "rb", "wb", "ab"

File handling methods and operations

Data in a file is brought from the storage device into RAM, where the file can be created, read, updated and closed. Python's file handling methods are:

  • open() gives access to a file. It takes 2 arguments: the file name (with the complete path if it is in another folder) and the mode of operation (reading, writing, appending and so on). It returns a file handler (file object) that is used for every later operation.
  • read() extracts the contents of a file opened in read mode; the complete contents are returned as one string. reads one line and returns a list of lines.
  • write() adds new material to a file opened in write or append mode.
  • close() closes the file when work is finished. This guarantees data integrity (all data is really saved) and releases the system memory used by the file.
File modes in Python
ModeMeaningIf the file does not existIf the file exists
"r"read (default)FileNotFoundErrorreads from the start
"w"writecreates itold contents are erased
"a"appendcreates itnew data is added at the end
"x"createcreates itFileExistsError
"r+"read and writeFileNotFoundErrorreads and writes
"rb", "wb", "ab"the same modes for binary filesas aboveas above
Fig. 3.11: create a file and write text in it
Create the file abc.txt and write one sentence in it.
new_file = open("abc.txt", "w")
new_file.write("The quick brown fox jumps over the lazy dog !!!")

print("File created and written to successfully!")
new_file.close()
Fig. 3.11
File created and written to successfully!
Output
The quick brown fox jumps over the lazy dog !!!
Contents of abc.txt (opened in Notepad)
  • Line 1: creates abc.txt (if it does not already exist) and returns the file handler .
  • Line 2: writes the sentence through the file handler.
  • Line 4 tells the user that the file can now be opened and checked.
  • Line 5: every opened file must be closed explicitly before the program ends; this is generally the last line.
Fig. 3.12: opening a file with the 'with' keyword
Write the same sentence (without '!!!') using with.
with open("abc.txt", "w") as file:
    file.write("The quick brown fox jumps over the lazy dog")

print("File created and written to successfully!")
Fig. 3.12
File created and written to successfully!
Output
The quick brown fox jumps over the lazy dog
Contents of abc.txt: the new text has replaced the old

The benefit of the keyword is that the file does not need to be closed explicitly: it is closed automatically when the indented block ends, even if an error occurs inside the block. Because mode "w" was used, the old sentence with '!!!' is overwritten.

Fig. 3.13: read an existing file
Open abc.txt in read mode and print it character by character.
with open("abc.txt", "r") as file:
    for char in file.read():
        print(char, end="")
print("\nFile read character by character successfully!")
Fig. 3.13
The quick brown fox jumps over the lazy dog
File read character by character successfully!
Output

Line 1 opens the file in read mode (an error occurs if the file is missing). Line 2: returns the whole text as a string and the for loop takes it one character at a time. Line 3 prints each character with so no new line is added after each character. Line 4 prints the confirmation on a new line ().

Handling file errors with try-except

Errors may arise while working with files, for example opening a file that does not exist, reading from a file, or problems when writing data. Such an error normally ends the program abnormally. To avoid this, file operations are placed in a try block and particular exceptions such as FileNotFoundError, PermissionError or IOError are handled in except blocks.

Fig. 3.14: try-except block
Read abc.txt safely.
try:
    with open("abc.txt", "r") as file:
        for char in file.read():
            print(char, end="")
    print("\nFile read successfully")
except FileNotFoundError:
    print("Error: The file does not exist.")
except IOError:
    print("Error: An issue occurred while reading the file.")
Fig. 3.14
The quick brown fox jumps over the lazy dog
File read successfully
Output when abc.txt exists
Error: The file does not exist.
Output when abc.txt does not exist

If the file is missing, Python jumps from line 2 straight to the matching block, prints a friendly message and the program ends normally instead of crashing.

Appending and reading line by line

with open("notes.txt", "w") as f:            # "w" creates the file (or empties it)
    f.write("Line 1: Lists\n")
with open("notes.txt", "a") as f:            # "a" adds at the end, old text is kept
    f.write("Line 2: Files\n")
    f.write("Line 3: Databases\n")

with open("notes.txt", "r") as f:
    print(f.readline(), end="")              # readline(): one line
    rest = f.readlines()                     # readlines(): list of the remaining lines
print(rest)
Modes w, a and r with readline() and readlines()
Line 1: Lists
['Line 2: Files\n', 'Line 3: Databases\n']
Output

A binary file is opened with a "b" in the mode, and Python reads and writes bytes instead of text:

data = bytes([72, 105, 33, 0, 255])          # five raw byte values 0 to 255
with open("data.bin", "wb") as f:            # "wb": write binary
    f.write(data)
with open("data.bin", "rb") as f:            # "rb": read binary
    content = f.read()
print(content)
print(list(content), len(content), "bytes")
Writing and reading a binary file
b'Hi!\x00\xff'
[72, 105, 33, 0, 255] 5 bytes
Output
Activity 6: copy a file character by character
Open abc.txt in read mode, create another file xyz.txt and write the contents of abc.txt into it by reading character by character.
try:
    with open("abc.txt", "r") as source, open("xyz.txt", "w") as target:
        count = 0
        for char in source.read():       # read character by character
            target.write(char)           # write each character to xyz.txt
            count += 1
    print(count, "characters copied from abc.txt to xyz.txt")
    with open("xyz.txt", "r") as check:
        print("xyz.txt contains:", check.read())
except FileNotFoundError:
    print("Error: abc.txt does not exist.")
43 characters copied from abc.txt to xyz.txt
xyz.txt contains: The quick brown fox jumps over the lazy dog
Output

3.2.8GUI in Python

A graphical user interface (GUI) lets the user work with a program through windows, menus, text fields and buttons, instead of typing commands as in a command-line interface (CLI). A GUI improves usability and accessibility.

Tkinter is Python's built-in library for creating GUIs. It handles common tasks such as taking user input and deciding what happens when a button is clicked. With Tkinter programmers build desktop applications (word processors, media players, basic utilities) and simple 2D games. The arrangement of GUI components and the logic behind them are generally kept separate in the code.

Widget
A basic component of the interface: labels, buttons, text boxes (Entry), check boxes. Widgets are arranged and modified to get the required layout.
Event handling
Writing code that responds to events such as mouse clicks, key presses and window resizing. A Button's names the function to run when it is clicked.
Main loop
keeps the window open, waiting for and handling events. Without it the window would just flash and disappear.
Main Tkinter functions used in the unit
Tkinter itemPurpose
creates the main window that holds all components
sets the window title
shows text
a one-line text box for input; reads it, clears it
a button that calls function f when clicked
/ place a widget in a row and column of a grid, or one below another
changes a widget's option (for example its text or colour) while the program runs

Simple number adder with GUI

Steps to create an adder that takes two numbers and shows their sum when the Add button is pressed:

  1. Import the library: .
  2. Define : read both boxes with , convert them to numbers and show the sum in the result label; if the user types letters, a ValueError is caught and an error message is shown.
  3. Define : delete the text of both boxes from position 0 to the end and reset the label.
  4. Create the main window: and .
  5. Create two Entry boxes, 10 characters wide, in row 0 (columns 0 and 1) with 5 pixels of padding (, ).
  6. Add the buttons in row 1: "Add" calls and "Clear" calls .
  7. Add the result label in row 2, spread across both columns with .
  8. Run the GUI with .
import tkinter as tk

def add_numbers():
    try:
        num1 = float(entry1.get())
        num2 = float(entry2.get())
        result_label.config(text=f"Sum: {int(num1 + num2)}")
    except ValueError:
        result_label.config(text="Invalid input. Please enter numbers.")

def clear_fields():
    entry1.delete(0, tk.END)
    entry2.delete(0, tk.END)
    result_label.config(text="Result will be displayed here.")

root = tk.Tk()
root.title("Simple Adder")

entry1 = tk.Entry(root, width=10)
entry1.grid(row=0, column=0, padx=5, pady=5)

entry2 = tk.Entry(root, width=10)
entry2.grid(row=0, column=1, padx=5, pady=5)

add_button = tk.Button(root, text="Add", command=add_numbers)
add_button.grid(row=1, column=0, padx=5, pady=5)

clear_button = tk.Button(root, text="Clear", command=clear_fields)
clear_button.grid(row=1, column=1, padx=5, pady=5)

result_label = tk.Label(root, text="Result will be displayed here.")
result_label.grid(row=2, column=0, columnspan=2, padx=5, pady=5)

root.mainloop()
Fig. 3.15: the complete Simple Adder
Output: the Simple Adder window after typing 1234 and 4321 and clicking Add.

Developing a game using tkinter: the colour game

The word shown is the name of one colour, but it is displayed in a different (random) colour to confuse the player. The player must type the colour of the text, spelled correctly. Only a correct colour increases the score.

  1. Create a Python file and import and .
  2. Set a list of colours.
  3. Game logic: set . picks a random word and a random display colour , shows the word in that colour and clears the entry. compares the player's input with ; if correct the score increases, otherwise "(Incorrect)" is shown; then a new round starts.
  4. GUI: main window titled "Color Game", a label for the word, an entry for the answer, a Check button and a score label.
  5. Start: call for the first round, then .
import tkinter as tk
import random

window = tk.Tk()
window.title("Color Game")
score = 0
colors = ["red", "blue", "green", "yellow", "pink", "black",
          "orange", "purple", "brown"]

def new_round():
    global score
    global color_to_guess, random_color
    color_to_guess = random.choice(colors)
    random_color = random.choice(colors)
    label.config(text=color_to_guess, fg=random_color)
    entry.delete(0, tk.END)

def check_answer():
    global score
    user_input = entry.get().lower()
    if user_input == random_color:
        score += 1
        score_label.config(text="Score: " + str(score))
    else:
        score_label.config(text="Score: " + str(score) + " (Incorrect)")
    new_round()

label = tk.Label(window, font=("Arial", 24))
label.pack(pady=20)

entry = tk.Entry(window, font=("Arial", 18))
entry.pack()

check_button = tk.Button(window, text="Check", command=check_answer)
check_button.pack(pady=10)

score_label = tk.Label(window, text="Score: 0")
score_label.pack()

new_round()
window.mainloop()
Fig. 3.16: the colour game

Output: a window titled "Color Game" shows a colour name (for example "pink") written in another colour, an entry box, a Check button and the score line, such as "Score: 1 (Incorrect)" after a wrong answer. The keyword is needed because the functions change variables created outside them.

Pygame

Pygame is a Python library (installed with ) mainly used for 2D games. It opens a window, draws shapes and images, and reads keyboard and mouse events inside a game loop that runs many times per second. The steps for a Pygame GUI program are: import and initialise (), create the window (), run the game loop (handle events, update, draw), refresh the screen () and finally . A complete Pygame program is given in the answers to ERQ 7 and Mini project 1.

3.2.9DatabasesOften examined

A database is a set of data organised in a systematic manner, usually in a computer system. It stores, retrieves and handles a large number of records for multiple users, and lets users find specific information quickly and accurately. A database is the central source of data for almost every software program: when a user logs in, shops online or makes a transaction, the database saves the relevant data (login time, items purchased, account used) so it can be retrieved later.

Why databases are used

  • They remove the redundancy (repetition) and inconsistency that are common in manual record keeping.
  • They ensure the reliability of data.
  • They allow concurrent access: several users can work on the same data at the same time.
  • They provide security measures to protect sensitive information from unwanted access.
  • They give effective information retrieval and help in data analysis.

Types of databases

Most databases are relational
Relational databaseNoSQL database
Organises data into tables of rows and columns; each table represents one entity (customers, items).Does not have a fixed table structure.
Fixed schema; tables linked by keys.Flexible; handles vast amounts of unstructured or semi-structured data.
Queried with SQL. Examples: MySQL, SQLite, PostgreSQL, MS Access.Examples: MongoDB (documents), Redis (key-value).

Key concepts

Table
The fundamental structure of a relational database, made of rows and columns. Each row is a record and each column is a field.
Record
A single row of a table holding information about one entity; for example a client record with name, address and telephone number.
Field
A piece of data within a record that defines the information placed in one column; for example a Name field stores text and an Age field stores a number.
Data integrity
The accuracy and consistency of data in a database, so that the data is correct, dependable and fit for use.

Primary and secondary keys

Keys uniquely identify records and create links among tables. The most common keys used in normalization are:

Primary key
A minimal set of attributes that uniquely identifies each record of a table. It can be one attribute or a combination of attributes. It cannot be empty (NULL) or repeated. Example: in the Customer table.
Candidate key
A single attribute or set of attributes that can identify a record uniquely. A table may have several candidate keys; one is chosen as the primary key and the rest are called secondary (alternate) keys. Example: in a Student table both RollNo and CNIC are candidate keys.
Foreign key
A field in one table that refers to the primary key of another table, creating a relationship between them. Example: in the Order table refers to the Customer table.
Composite key
A key made of two or more attributes that together identify a row uniquely. Example: (OrderID, L_ID) when one order can contain several laptops.

Data normalization

Data normalization is a database design technique that organises data into smaller, inter-linked tables to reduce redundancy (duplicate storage of the same data) and dependency. Redundant data causes problems in keeping data consistent and increases the chance of mistakes during updates. For example, if a customer's address is stored in many rows, every address change must be made in many places; missing one leaves the data inconsistent. Normalization stores the address once, in a Customer table, linked to other tables by a key such as the customer ID. Every table then represents one entity and keys define the relationships.

Normalization steps
Normal formRule
First normal form (1NF)Every cell holds a single (atomic) value; repeating groups and multi-valued cells are removed.
Second normal form (2NF)The table is in 1NF and every non-key attribute depends on the whole primary key (no partial dependency on part of a composite key).
Third normal form (3NF)The table is in 2NF and no non-key attribute depends on another non-key attribute (no transitive dependency).

Worked example: a laptop shop database

Step 1: identify the entities and their attributes. Customer: C_ID, C_Name, C_Address, C_Phone. Laptop: L_ID, L_Name, L_Price, L_Brand. Order: OrderID, OrderDate, C_ID, L_ID, Quantity.

Step 2: the unnormalized data in one table. Customer and laptop details repeat in every order:

One table for everything: Qamar's and Mahtab's details, and the Dell and MacBook details, are stored twice
OrderIDOrderDateC_IDC_NameC_AddressC_PhoneL_IDL_NameL_PriceL_BrandQty
120-11-2024C1QamarHouse: 123123-4567L1Dell XPS 13100000Dell1
221-11-2024C2MahtabHouse: 456987-6543L2MacBook Air90000Apple2
322-11-2024C1QamarHouse: 123123-4567L3HP Spectre x360120000HP1
423-11-2024C2MahtabHouse: 456987-6543L1Dell XPS 13100000Dell1
524-11-2024C3ChaandHouse: 789543-2109L2MacBook Air90000Apple2

Step 3: 1NF. The repeating groups are the customer information and the laptop information repeated for each order. They are moved to separate tables: Customer(C_ID, C_Name, C_Address, C_Phone), Laptop(L_ID, L_Name, L_Price, L_Brand) and Order(OrderID, OrderDate, C_ID, L_ID, Quantity).

Customer
C_IDC_NameC_AddressC_Phone
C1QamarHouse: 123123-4567
C2MahtabHouse: 456987-6543
C3ChaandHouse: 789543-2109
Laptop
L_IDL_NameL_PriceL_Brand
L1Dell XPS 13100000Dell
L2MacBook Air90000Apple
L3HP Spectre x360120000HP

Step 4: 2NF. An order may contain several laptops, so a row of the Order table is identified by the composite key (OrderID, L_ID). OrderDate depends only on OrderID, which is part of the key: a partial dependency. It is moved to a new table OrderDetails(OrderID, OrderDate), and the Order table keeps (OrderID, C_ID, L_ID, Quantity).

OrderDetails
OrderIDOrderDate
120-11-2024
221-11-2024
322-11-2024
423-11-2024
524-11-2024
Order
OrderIDC_IDL_IDQuantity
1C1L11
2C2L22
3C1L31
4C2L11
5C3L22

Step 5: 3NF. In the original table, L_Name, L_Price and L_Brand depend on L_ID, and L_ID depends on the order: a transitive dependency (OrderID L_ID L_Price). The same is true of the customer details through C_ID. In the tables above the Order table only references L_ID and C_ID (foreign keys) and every laptop or customer detail is stored once, so the four tables Customer, Laptop, Order and OrderDetails are in 3NF.

Database tools

Two popular database tools
Microsoft AccessSQLite
Part of the MS Office suite; simple to install and easy to use with a drag-and-drop interface.A lightweight database system that stores the whole database in a single file.
Used by individuals and small organisations for inventory, project management and event planning.Ideal for applications needing local storage, widely used in mobile apps and where no large database server is needed.
Integrates with Excel and Project.Simple, efficient and easy to use; built into Python as the sqlite3 module.

Advantages of SQLite: it occupies little space, so it suits applications with limited resources; it is easily integrated into applications, so it is easy to use and deploy; it has a serverless architecture that removes unnecessary dependencies; and the complete database is saved in a single file.

3.2.10Create a database with SQLiteOften examined

1. Installation of SQLite

Download SQLite (DB Browser for SQLite) for your Windows version from the official website (https://sqlitebrowser.org/dl/). The download is a compressed (ZIP) file: right-click it and choose "Extract All" to get a folder of ready-to-use files. To check the installation, open the Command Prompt (type cmd in the Windows search bar), move to the extracted folder with the command (for example ), type and press Enter. If SQLite's own prompt appears, it is installed.

2. Database creation

Open the terminal, type and press Enter to enter the SQLite shell. The command creates a new database file NBF.db in the current folder (or opens it if it exists).

C:\Downloads\sqlite> sqlite3
sqlite> .open NBF.db
Creating the database NBF.db from the SQLite command-line interface

3. Creating a table

takes the table name, the column names and their data types. The table Laptop has four columns: L_ID (text, primary key), L_Name (text), L_Price (integer) and L_Brand (text). means the column cannot be left empty. At this stage only the schema (structure) exists; the table has no records.

CREATE TABLE Laptop (
    L_ID TEXT PRIMARY KEY,
    L_Name TEXT NOT NULL,
    L_Price INTEGER NOT NULL,
    L_Brand TEXT NOT NULL
);
Fig. 3.19: creating a table

4. Adding records

adds a row. It needs the table name, the column names and the values, in the same sequence as the columns.

INSERT INTO Laptop (L_ID, L_Name, L_Price, L_Brand)
VALUES ('L1', 'Dell XPS 13', 100000, 'Dell');
Fig. 3.20: adding a single record

Several records can be added with a single command:

INSERT INTO Laptop (L_ID, L_Name, L_Price, L_Brand)
VALUES
    ('L2', 'MacBook Air', 90000, 'Apple'),
    ('L3', 'HP Spectre x360', 120000, 'HP');
Fig. 3.21: adding multiple records
L_ID  L_Name           L_Price  L_Brand
----  ---------------  -------  -------
L1    Dell XPS 13      100000   Dell
L2    MacBook Air      90000    Apple
L3    HP Spectre x360  120000   HP
The Laptop table now holds three records
Activity 7: populate the Customer, Order and OrderDetails tables
Create and fill the tables Customer, Order and OrderDetails of the laptop shop (3NF design).
CREATE TABLE Customer (C_ID TEXT PRIMARY KEY, C_Name TEXT NOT NULL, C_Address TEXT, C_Phone TEXT);
CREATE TABLE OrderDetails (OrderID INTEGER PRIMARY KEY, OrderDate TEXT NOT NULL);
CREATE TABLE "Order" (OrderID INTEGER, C_ID TEXT, L_ID TEXT, Quantity INTEGER,
    PRIMARY KEY (OrderID, L_ID),
    FOREIGN KEY (OrderID) REFERENCES OrderDetails(OrderID),
    FOREIGN KEY (C_ID) REFERENCES Customer(C_ID));

INSERT INTO Customer VALUES
    ('C1', 'Qamar', 'House: 123', '123-4567'),
    ('C2', 'Mahtab', 'House: 456', '987-6543'),
    ('C3', 'Chaand', 'House: 789', '543-2109');
INSERT INTO OrderDetails VALUES
    (1, '20-11-2024'), (2, '21-11-2024'), (3, '22-11-2024'), (4, '23-11-2024'), (5, '24-11-2024');
INSERT INTO "Order" VALUES
    (1, 'C1', 'L1', 1), (2, 'C2', 'L2', 2), (3, 'C1', 'L3', 1), (4, 'C2', 'L1', 1), (5, 'C3', 'L2', 2);
Creating and populating the three tables

To check that the tables are linked correctly, a query can join them through their keys:

SELECT o.OrderID, d.OrderDate, c.C_Name, o.L_ID, o.Quantity
FROM "Order" o
JOIN OrderDetails d ON o.OrderID = d.OrderID
JOIN Customer c ON o.C_ID = c.C_ID;
OrderID  OrderDate   C_Name  L_ID  Quantity
-------  ----------  ------  ----  --------
1        20-11-2024  Qamar   L1    1
2        21-11-2024  Mahtab  L2    2
3        22-11-2024  Qamar   L3    1
4        23-11-2024  Mahtab  L1    1
5        24-11-2024  Chaand  L2    2
Output

OrderDetails has OrderID as its primary key. In Order the composite key is (OrderID, L_ID), and OrderID and C_ID are foreign keys that point to OrderDetails and Customer.

5. Querying records with SELECT

is the most used query. means all columns; alternatively list the column names to show only those columns. A clause adds a condition, so only the matching record(s) are fetched.

SELECT * FROM Customer;
Fig. 3.22: select all records
C_ID  C_Name  C_Address   C_Phone
----  ------  ----------  --------
C1    Qamar   House: 123  123-4567
C2    Mahtab  House: 456  987-6543
C3    Chaand  House: 789  543-2109
Output
SELECT * FROM Customer
WHERE C_ID = 'C2';
Fig. 3.23: select with a condition
C_ID  C_Name  C_Address   C_Phone
----  ------  ----------  --------
C2    Mahtab  House: 456  987-6543
Output
SELECT L_Name, L_Price FROM Laptop
WHERE L_Price >= 100000;
Selecting chosen columns
L_Name           L_Price
---------------  -------
Dell XPS 13      100000
HP Spectre x360  120000
Output

6. Update record

changes a record or a column. Good practice: first run a SELECT with the same WHERE clause to see which record will change, then run the UPDATE. Without WHERE, every row would be updated.

UPDATE Customer
SET C_Address = 'New Address'
WHERE C_ID = 'C1';
Fig. 3.24: update record
C_ID  C_Name  C_Address    C_Phone
----  ------  -----------  --------
C1    Qamar   New Address  123-4567
C2    Mahtab  House: 456   987-6543
C3    Chaand  House: 789   543-2109
Customer table after the update

7. Use of ORDER BY clause

displays the result in sorted order, ascending by default. The keyword sorts in descending order.

SELECT * FROM Customer
ORDER BY C_Name;
Fig. 3.25 (a): ascending order
C_ID  C_Name  C_Address    C_Phone
----  ------  -----------  --------
C3    Chaand  House: 789   543-2109
C2    Mahtab  House: 456   987-6543
C1    Qamar   New Address  123-4567
Output
SELECT * FROM Customer
ORDER BY C_Name DESC;
Fig. 3.25 (b): descending order
C_ID  C_Name  C_Address    C_Phone
----  ------  -----------  --------
C1    Qamar   New Address  123-4567
C2    Mahtab  House: 456   987-6543
C3    Chaand  House: 789   543-2109
Output

8. Deletion of records

removes one or more records. The table structure stays in the database. Like SELECT, it works on all records if there is no WHERE clause, and only on the selected records with a WHERE clause. matches any value in a list. The name Order is written in quotes because ORDER is an SQL keyword.

DELETE FROM "Order" WHERE L_ID IN ('L1', 'L2', 'L3');
Fig. 3.26: deletion of records
OrderID  C_ID  L_ID  Quantity
-------  ----  ----  --------
(no rows)
The Order table is now empty, but it still exists

Connecting Python to a database

Python's built-in module lets a program use a database directly. The steps are: connect, create a cursor, execute SQL, commit the changes, fetch results and close the connection. The placeholders pass values safely into the SQL command.

import sqlite3

conn = sqlite3.connect("school.db")          # 1. connect (creates the file if needed)
cur = conn.cursor()                          # 2. cursor: sends SQL to the database

cur.execute("""CREATE TABLE IF NOT EXISTS Student (
                   RollNo INTEGER PRIMARY KEY,
                   Name TEXT NOT NULL,
                   Marks INTEGER)""")        # 3. run SQL commands
cur.execute("INSERT INTO Student VALUES (?, ?, ?)", (1, "Ali", 78))
cur.execute("INSERT INTO Student VALUES (?, ?, ?)", (2, "Sana", 91))
conn.commit()                                # 4. save the changes permanently

cur.execute("SELECT * FROM Student")
for row in cur.fetchall():                   # 5. fetch the result rows
    print(row)
conn.close()                                 # 6. close the connection
Using SQLite from Python
(1, 'Ali', 78)
(2, 'Sana', 91)
Output

3.3Techniques for testing and debuggingOften examined

Testing checks whether a program gives the correct results; it ensures the correctness of code. Debugging is the process of analysing the code to find the bugs (errors) that testing reveals, locating them and fixing them, so that the error-free program runs smoothly and gives the desired results.

Kinds of errors found by testing and debugging
Error typeWhat happensExample
Syntax errorThe code breaks Python's grammar rules; the program does not start. (missing bracket)
Runtime error (exception)The program starts but stops when an illegal operation happens., KeyError, FileNotFoundError
Logical errorThe program runs but gives a wrong answer. Hardest to find; debugging tools are needed.swapped indexes in a transpose

3.3.1DebuggingOften examined

Debugging with print statements

The simplest debugging method is to add print statements to check whether the program is behaving as expected. This does not mean a print after every line. Wherever a formula, an expression or a critical value update occurs, a print statement shows whether that variable, line or block is working as desired. Each print is labelled so we know which one produced the output.

Fig. 3.27: debugging with print statements
In the transpose program below a bug has been placed on purpose in line 8: the indexes are switched. When run, the transpose comes out the same as the matrix. Use print statements to find the bug.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transpose = []

for col in range(len(matrix[0])):
    new_row = []
    print('Print1-Outer Loop: ', new_row)
    for row in range(len(matrix)):
        new_row.append(matrix[col][row])
        print('Print2-Inner Loop: row: ', row, '- col: ', col)
    print('Print3-Outer Loop: ', new_row)
    transpose.append(new_row)
for row in transpose:
    print(row)
Fig. 3.27 (with the bug)
Print1-Outer Loop:  []
Print2-Inner Loop: row:  0 - col:  0
Print2-Inner Loop: row:  1 - col:  0
Print2-Inner Loop: row:  2 - col:  0
Print3-Outer Loop:  [1, 2, 3]
Print1-Outer Loop:  []
Print2-Inner Loop: row:  0 - col:  1
Print2-Inner Loop: row:  1 - col:  1
Print2-Inner Loop: row:  2 - col:  1
Print3-Outer Loop:  [4, 5, 6]
Print1-Outer Loop:  []
Print2-Inner Loop: row:  0 - col:  2
Print2-Inner Loop: row:  1 - col:  2
Print2-Inner Loop: row:  2 - col:  2
Print3-Outer Loop:  [7, 8, 9]
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
Output
  • 'Print1' (line 6) shows that is reset to [ ] each time the outer loop index changes, so this part is correct.
  • 'Print2' (line 9) shows the index values inside the inner loop. While is 0, goes 0, 1, 2.
  • 'Print3' (line 10) shows that the first new row is [1, 2, 3], which is the first row of the matrix, not the first column [1, 4, 7]. So line 8 reads (a row) when it should read (a column). The bug is found.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transpose = []

for col in range(len(matrix[0])):
    new_row = []
    for row in range(len(matrix)):
        new_row.append(matrix[row][col])     # bug fixed: row first, then col
    transpose.append(new_row)
for row in transpose:
    print(row)
After the fix
[1, 4, 7]
[2, 5, 8]
[3, 6, 9]
Output: the correct transpose

Advanced debugging: breakpoints and watchpoints

Advanced debugging uses tools of an IDE (such as Visual Studio, VS Code or the online IDE Replit) instead of print commands:

Breakpoint
A marker placed on a line of code. When the program is run in debug mode, it pauses just before that line, so the programmer can inspect the current values of variables and data structures.
Watchpoint (watch)
Set on a variable, expression or formula. The debugger shows its current value every time the program pauses, and can flag when it changes or differs from the expected value.
Stepping
Running the paused program one line at a time (step over, step into) to follow its flow.

Several breakpoints and watchpoints can be placed in one program; these methods are especially useful for complex problems. In Fig. 3.28 the transpose code is run in Replit's debugger with a breakpoint on line 7. At each pause the Variables panel shows col, row, new_row, matrix and transpose, which helps analyse the data and the behaviour of the program.

Debugger commands (IDEs give the same actions as buttons)
Command (Python debugger, pdb)Meaning
written in the code: pause here
set a breakpoint on line 7
continue to the next breakpoint
/ next line (step over) / step into a function
print the value of x
watch x: show it whenever it changes
quit the debugger

3.3.2Python unit testOften examined

Unit testing checks small, isolated parts (units) of a program, such as a single function or method, to see that each works correctly. Instead of running the program by hand and checking values each time (which becomes difficult as the code grows), we write small tests once and run them again whenever the code changes. Python's built-in unittest framework supports this.

For a function that performs some operation, we write another function (a test function) that calls it with chosen inputs and uses assertions to compare the actual output with the expected output. The statement does nothing if the condition is True; if it is False it stops the program with an AssertionError that shows the message.

Fig. 3.29: function to add 2 numbers with tests
Test add() with two positive numbers, two zeros and a negative with a positive number.
def add(a, b):                  # Function to be tested
    return a + b

def test_add():                 # Test function
    result = add(2, 3)          # Test case 1: Check if 2 + 3 equals 5
    assert result == 5, f"Test failed: Expected 5 but got {result}"

    result = add(0, 0)          # Test case 2: Check if 0 + 0 equals 0
    assert result == 0, f"Test failed: Expected 0 but got {result}"

    result = add(-1, 1)         # Test case 3: Check if -1 + 1 equals 0
    assert result == 0, f"Test failed: Expected 0 but got {result}"

    print("All tests passed!")

test_add()                      # Run the test function
Fig. 3.29
All tests passed!
Output

Line 1 defines . The test function on line 4 calls it with three sets of inputs (lines 5, 8 and 11). Each assert checks the result against the expected value. All three pass, so line 14 prints the message.

Now suppose the expected result on line 12 is wrongly written as 2 (the negative value on line 11 was overlooked). The test fails:

def add(a, b):                  # Function to be tested
    return a + b

def test_add():                 # Test function
    result = add(2, 3)          # Test case 1: Check if 2 + 3 equals 5
    assert result == 5, f"Test failed: Expected 5 but got {result}"

    result = add(0, 0)          # Test case 2: Check if 0 + 0 equals 0
    assert result == 0, f"Test failed: Expected 0 but got {result}"

    result = add(-1, 1)         # Test case 3: Check if -1 + 1 equals 0
    assert result == 2, f"Test failed: Expected 2 but got {result}"

    print("All tests passed!")

test_add()                      # Run the test function
Line 12 changed to expect 2
Traceback (most recent call last):
  File "prog.py", line 16, in <module>
    test_add()                      # Run the test function
    ^^^^^^^^^^
  File "prog.py", line 12, in test_add
    assert result == 2, f"Test failed: Expected 2 but got {result}"
           ^^^^^^^^^^^
AssertionError: Test failed: Expected 2 but got 0
Output: test case 3 fails

To investigate, the book places breakpoints on lines 6, 9 and 12 and adds the variable as a watch (Fig. 3.30). At the first breakpoint the watch shows result = 5 (as expected, Fig. 3.31) and at the second result = 0 (as expected, Fig. 3.32). At the third breakpoint the watch shows result = 0 while line 12 expects 2, so the debugger flags the mismatch and points to the bug (Fig. 3.33).

Fig. 3.34: function to multiply 2 numbers with tests
The same code is changed for multiplication and the tests are adjusted. Will all tests pass? If not, which one will fail and why?
def multiply(a, b):
    return a * b

def test_multiply():
    result = multiply(2, 3)
    assert result == 6, f"Test failed: Expected 6 but got {result}"

    result = multiply(4, 0)
    assert result == 1, f"Test failed: Expected 1 but got {result}"

    result = multiply(-1, -1)
    assert result == 1, f"Test failed: Expected 1 but got {result}"

    print("All tests passed!")

test_multiply()
Fig. 3.34
Traceback (most recent call last):
  File "prog.py", line 16, in <module>
    test_multiply()
  File "prog.py", line 9, in test_multiply
    assert result == 1, f"Test failed: Expected 1 but got {result}"
           ^^^^^^^^^^^
AssertionError: Test failed: Expected 1 but got 0
Output

Test case 1 passes (). Test case 2 fails: , but line 9 expects 1, so the assertion is False and the program stops with "Test failed: Expected 1 but got 0". Test case 3 is never reached (it would pass, since ). The bug is in the test, not in the function: line 9 should be . After this change all tests pass.

The unittest module

The unittest framework groups tests into a class derived from . Each method whose name starts with is one test case, and methods such as compare actual and expected results. runs all tests and reports which passed or failed.

import unittest

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

class TestAdd(unittest.TestCase):          # a group of test cases
    def test_positive(self):
        self.assertEqual(add(2, 3), 5)

    def test_zero(self):
        self.assertEqual(add(0, 0), 0)

    def test_negative(self):
        self.assertEqual(add(-1, 1), 0)

unittest.main(verbosity=2)                 # finds and runs every test_ method
A unit test with the unittest module
test_negative (__main__.TestAdd.test_negative) ... ok
test_positive (__main__.TestAdd.test_positive) ... ok
test_zero (__main__.TestAdd.test_zero) ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
Output (the time may differ slightly)

Key terms

Programming paradigm
A style or methodology of writing and organising program code that decides its structure, flow and data management.
Complexity reduction
Breaking a complex problem into simpler parts (functions, objects, modules) that are easier to understand and solve.
Functional programming
A paradigm in which the program is built from functions; it focuses on what to compute and avoids changing state.
Object oriented programming (OOP)
A paradigm based on objects that combine data (attributes) and behaviour (methods), created from classes.
Class
A template or stencil that defines the attributes and methods of its objects.
Object
An instance of a class with its own attribute values, representing a real-world thing.
Attribute
A variable that stores a property (data) of an object.
Method
A function defined inside a class that describes the behaviour of its objects.
Encapsulation
Bundling data and the methods that use it into one class and restricting direct access to the data.
Abstraction
Showing only the essential features of an object and hiding the implementation details.
Inheritance
Creating a new (derived) class from an existing (parent) class so that it reuses the parent's attributes and methods.
Polymorphism
The ability of objects of different classes to respond to the same method call in their own way.
List
An ordered, mutable collection of items written in square brackets and accessed by index.
Tuple
An ordered, immutable collection written in round brackets; it cannot be changed after creation.
Set
An unordered collection of unique elements written in curly brackets; duplicates are removed automatically.
Dictionary
A collection of key-value pairs in which every key is unique and immutable and is used to look up its value.
Nested list
A list whose elements are lists; used to represent matrices and tables.
Transpose
A matrix obtained by swapping the rows and columns of another matrix.
Disk I/O
Reading data from and writing data to a storage disk; the bridge between the CPU and permanent storage.
Text file
A file that stores readable characters, with lines ended by an end-of-line marker.
Binary file
A file that stores raw bytes (0s and 1s), such as images, audio, video and programs.
File handler
The file object returned by open(), used for reading, writing and closing the file.
File mode
The second argument of open() that states the operation: r, w, a, x, r+, or their binary forms rb, wb, ab.
Exception
An error that occurs while a program runs, such as FileNotFoundError; handled with try-except.
GUI
Graphical user interface: interaction through windows, buttons, text fields and menus instead of typed commands.
Tkinter
Python's built-in library for creating GUI applications.
Widget
A basic GUI component such as a label, button, entry box or check box.
Pygame
A Python library used mainly for 2D games; it draws graphics and handles keyboard and mouse events.
Database
An organised collection of data that stores, retrieves and manages many records for multiple users.
Relational database
A database that stores data in tables of rows and columns linked by keys.
NoSQL database
A database without a fixed table structure, for large unstructured or semi-structured data.
Table
The basic structure of a relational database, made of rows (records) and columns (fields).
Record
One row of a table, holding information about one entity.
Field
One column of a table; a single piece of data in a record.
Data integrity
The accuracy and consistency of data in a database.
Primary key
A minimal set of attributes that uniquely identifies each record of a table; never repeated or empty.
Candidate key
An attribute or set of attributes that can uniquely identify a record; one is chosen as the primary key.
Secondary key
A candidate key that was not chosen as the primary key.
Foreign key
A field in one table that refers to the primary key of another table and links the two.
Composite key
A key made of two or more attributes that together identify a row uniquely.
Data normalization
Organising data into smaller related tables to reduce redundancy and dependency.
Redundancy
Unnecessary repetition of the same data in a database.
1NF
First normal form: every cell holds a single atomic value; repeating groups are removed.
2NF
Second normal form: 1NF plus every non-key attribute depends on the whole primary key.
3NF
Third normal form: 2NF plus no non-key attribute depends on another non-key attribute (no transitive dependency).
SQLite
A lightweight, serverless database system that stores the whole database in a single file.
SQL
Structured Query Language, used to create tables and to insert, select, update and delete records.
Testing
Running a program or part of it to check that it gives correct results.
Unit test
A test of one small, isolated unit of code such as a single function or method.
Assertion
A statement (assert) that checks a condition and stops with an AssertionError if it is false.
Debugging
Finding, locating and fixing bugs (errors) in a program.
Breakpoint
A marker on a line of code where the debugger pauses the program.
Watchpoint
A variable or expression whose value the debugger shows and monitors while the program runs.

Multiple choice questionsp. 54–55

1
Main purpose of the programming paradigm is ______.
  1. AIncrease code length
  2. BComplexity reductionAnswer
  3. CEliminate bugs
  4. DImprove hardware performance
Why

A paradigm organises code so that a complex problem is broken into simpler parts (functions or objects). It does not remove all bugs and has nothing to do with hardware.

2
______ programming paradigm emphasizes the use of functions without changing state.
  1. AProcedural
  2. BFunctionalAnswer
  3. CObject-oriented
  4. DStructural
Why

Functional programming builds the program from functions that return values without changing variables outside themselves (no change of state). Procedural code changes state step by step.

3
Object-Oriented Programming primarily revolves around ______ of the following.
  1. AData structures
  2. BFunctions
  3. CObjectsAnswer
  4. DVariables
Why

OOP models the problem as objects (made from classes) that combine data (attributes) and behaviour (methods).

4
In Python ______ data structure is used to store a collection of items.
  1. AListAnswer
  2. BDictionary
  3. CTuple
  4. DSet
Why

The list is Python's general-purpose ordered, changeable collection of items (Section 3.2.1). A dictionary stores key-value pairs, a tuple is fixed and a set holds only unique unordered items, so the expected answer is list.

5
A dictionary stores ______ in Python.
  1. AKeys only
  2. BValues only
  3. CKey-value pairsAnswer
  4. DSequential data
Why

Every entry of a dictionary links a unique key to a value, such as .

6
An example of disk I/O ______.
  1. AStoring a variable in memory
  2. BReading a file from the hard diskAnswer
  3. CDefining a function
  4. DLooping through a list
Why

Disk I/O is data moving between the disk and the CPU/RAM. Variables, functions and loops work only in memory (RAM).

7
To write content to a file in Python ______ method is used.
  1. Afile.write()Answer
  2. Bfile.insert()
  3. Cfile.add()
  4. Dfile.modify()
Why

writes a string to a file opened in "w" or "a" mode. Python file objects have no insert(), add() or modify() methods.

8
A primary key in a database is ______.
  1. AA key that has a default value
  2. BA unique identifier for recordsAnswer
  3. CA key for linking multiple databases
  4. DA key used for temporary tables
Why

A primary key uniquely identifies each record of a table; it cannot be repeated or empty. Linking tables is the job of a foreign key.

9
A common database normalization goal is ______.
  1. AReducing redundancyAnswer
  2. BMaximizing data size
  3. CIncreasing the number of tables
  4. DStoring duplicate records
Why

Normalization removes duplicate (redundant) data so that data stays consistent. More tables are a side effect, not the goal.

10
______ key is used to uniquely identify a record in a related table.
  1. APrimary
  2. BSecondary
  3. CForeignAnswer
  4. DUnique
Why

A foreign key is a field in one table that refers to the primary key of a related table, so it identifies exactly one record in that related table (for example C_ID in the Order table points to one customer). A primary key identifies records in its own table.

11
To create a simple GUI in Python, ______ library is used.
  1. APygameAnswer
  2. BOS
  3. CSys
  4. Dmath
Why

Of the options, only Pygame creates windows and graphics (the book's summary names Pygame). OS, sys and math have no GUI features. Tkinter, used in the book's examples, is also a GUI library but is not among the options.

12
A nested list in Python is referred to as ______.
  1. AA list containing another listAnswer
  2. BA list of dictionaries
  3. CA dictionary of lists
  4. DA set containing multiple lists
Why

A nested list is a list whose elements are lists, such as . A set cannot even contain lists, because lists are mutable.

13
The purpose of unit testing in Python is ______.
  1. ATest individual units of codeAnswer
  2. BTest the entire program
  3. CDebug the entire program
  4. DOptimize the code for speed
Why

A unit test checks one small isolated unit, such as a single function or method.

14
The primary goal of debugging is ______.
  1. AWriting new code
  2. BFixing errors in the codeAnswer
  3. CIncreasing program speed
  4. DCreating a graphical interface
Why

Debugging means locating and fixing bugs (errors) so that the program gives the desired results.

15
Python function used to print debugging messages to the console ______.
  1. Aprint()Answer
  2. Bdebug()
  3. Ctrace()
  4. Dlog()
Why

The simplest debugging technique places labelled \texttt{print()} statements in the code. Python has no built-in debug() or trace() function.

16
Output of the following code will be:
my_dict = {"a": [1, 2], "b": [3, 4]}
print(my_dict["a"][1])
  1. A1
  2. B2Answer
  3. C[1, 2]
  4. D[3, 4]
Why

\texttt{my\_dict["a"]} is the list [1, 2] and index [1] is its second element, 2.

2
Output
17
The code will print "Inner" ______ times.
for i in range(3):
    for j in range(2):
        print("Inner")
  1. A2
  2. B3
  3. C5
  4. D6Answer
Why

The outer loop runs 3 times and, for each of them, the inner loop runs 2 times, so the print runs 3\times2=6 times.

Short response questions (SRQs)p. 54

13 marks
Elaborate programming language paradigm and why is it important for software development?
Solution

A programming paradigm is a style or methodology of writing and organising program code. It decides how the code is structured, how the program flows and how data is managed. Examples are the functional paradigm (program built from functions) and the object-oriented paradigm (program built from objects).

  • It reduces complexity by breaking a big problem into smaller functions or objects.
  • It gives a standard structure, so programs are easier to read, test, debug and maintain.
  • It provides well-known tools and techniques, so teams can work together and focus on the task instead of inventing their own organisation.
23 marks
How do you interpret the concept of complexity reduction in programming? How does dividing code into modules help manage complexity?
Solution

Complexity reduction means breaking a large, complicated problem into smaller, simpler parts that are easier to understand and solve one at a time.

Dividing code into modules (functions, classes or separate files) helps because:

  • each module does one job, so it can be written, understood and tested separately;
  • a change or bug fix is made in one module only, without reading the whole program;
  • modules can be reused in other programs, and different programmers can work on different modules at the same time.

For example, a result-card program can have separate functions for reading marks, calculating the percentage and printing the card.

33 marks
Compare Functional Programming and Object-Oriented Programming (OOP) paradigms. Enlist main differences in their approach to problem-solving.
Solution
Functional programmingObject-oriented programming
The program is divided into functions.The program is divided into objects made from classes.
Focus on what is to be computed; data flows through functions.Focus on objects (data plus behaviour) that model real-world things.
Avoids changing state; functions have no side effects.Objects keep state (attribute values) that methods change.
Data and functions are separate.Data and methods are bundled together (encapsulation).
Code reused by combining functions.Code reused through inheritance and polymorphism.
Suits small applications, data processing and parallel tasks.Suits large, complex systems such as banking or games.
43 marks
What is Disk I/O and why is it important for system performance?
Solution

Disk I/O (input/output) is the reading of data from, and writing of data to, a storage disk such as a hard disk or SSD. It is the bridge between the CPU and the disk: data is brought from the disk into RAM for the CPU to process, and results are stored back on the disk permanently.

It is important for performance because almost every operation depends on it: launching programs, opening files and saving the user's changes. The disk is much slower than RAM and the CPU, so slow or excessive disk I/O makes the whole system wait. Efficient disk I/O (for example reading a file once instead of many times) keeps programs fast.

53 marks
Describe the different modes in file handling in Python.
Solution

The mode is the second argument of \texttt{open(filename, mode)} and tells Python what we want to do with the file:

  • "r" (read): opens an existing file for reading; the default mode. Error if the file does not exist.
  • "w" (write): creates a new file or erases an existing one, then writes to it.
  • "a" (append): adds new data at the end of the file without erasing the old contents; creates the file if it does not exist.
  • "x" (create): creates a new file; error if it already exists.
  • "r+": read and write the same file.
  • Adding b ("rb", "wb", "ab") opens a binary file such as an image.
f = open("marks.txt", "a")
f.write("Ali 85\n")
f.close()
Appending one line to a file
63 marks
What is the benefit of using the 'with' keyword while working with files in Python?
Solution

The \texttt{with} statement opens a file and closes it automatically when its indented block ends. We do not need to call \texttt{close()} ourselves.

Benefits: the file is closed even if an error occurs inside the block, so data is saved properly (data integrity) and the memory used by the file is released; the code is shorter and cleaner; and we cannot forget to close the file.

with open("abc.txt", "w") as file:
    file.write("Hello")
# the file is already closed here
73 marks
How can you create a simple GUI application using Pygame? Enlist the steps involved.
Solution
  1. Install and import the library: \texttt{pip install pygame}, then \texttt{import pygame}.
  2. Initialise it: \texttt{pygame.init()}.
  3. Create the window: \texttt{screen = pygame.display.set\_mode((400, 300))} and set its title with \texttt{pygame.display.set\_caption()}.
  4. Write the main (game) loop: handle events with \texttt{pygame.event.get()} (QUIT, key presses, mouse clicks).
  5. Update and draw: fill the background, draw shapes or text (for example \texttt{pygame.draw.rect()}).
  6. Refresh the display with \texttt{pygame.display.flip()} in every pass of the loop.
  7. When the loop ends, close the window with \texttt{pygame.quit()}.

A complete example is the answer to ERQ 7.

83 marks
Describe the use of a nested list in Python and how can it be used to represent a matrix?
Solution

A nested list is a list whose elements are themselves lists. It stores data in rows and columns, like a table, so it is used for matrices, game boards and marks of several students in several subjects.

To represent a matrix, each inner list is one row. The element in row r and column c is \texttt{m[r][c]} (indexes start at 0), and a nested loop visits every element.

m = [[1, 2, 3],
     [4, 5, 6]]
print(m[1][2])        # row 1, column 2
for row in m:
    print(row)
6
[1, 2, 3]
[4, 5, 6]
Output
92026 Annual I, Set B, Q.2 (vi)3 marks
What is data normalization and why is it used in database management?
Solution

Data normalization is a database design technique that divides data into smaller, related tables linked by keys, following rules called normal forms (1NF, 2NF, 3NF).

It is used to:

  • reduce redundancy, so the same data (such as a customer's address) is stored only once;
  • avoid inconsistencies and mistakes during insertion, update and deletion, since a change is made in one place only;
  • keep data accurate (data integrity) and make storage efficient and database access faster.

Example: storing customer details in a separate Customer table instead of repeating them in every order.

103 marks
What is debugging and why is it important in software development?
Solution

Debugging is the process of analysing a program to find the bugs (errors) in it, locating them and fixing them, so that the program runs smoothly and gives the desired results. Techniques include labelled print statements and debugger tools such as breakpoints and watchpoints.

It is important because even a small logical error can give wrong results (for example a wrong bill or wrong exam result), crash the program or create security problems. Debugging makes software correct and reliable, saves the cost of fixing errors after release, and helps programmers understand how their code actually behaves.

113 marks
What seems to be the problem in:
Sample_dict = {"a": 1, "b": 2}
print(sample_dict["c"])
Solution

There are two problems:

  • Python is case-sensitive: the dictionary is created as \texttt{Sample\_dict} (capital S) but line 2 uses \texttt{sample\_dict}, which does not exist, so a NameError occurs first.
  • Even with the name corrected, the key "c" is not in the dictionary (only "a" and "b"), so reading it raises a KeyError.
Traceback (most recent call last):
  File "prog.py", line 2, in <module>
    print(sample_dict["c"])
          ^^^^^^^^^^^
NameError: name 'sample_dict' is not defined. Did you mean: 'Sample_dict'?
Output as printed

Correction: use the same name and check the key first, or use get():

sample_dict = {"a": 1, "b": 2}
print(sample_dict.get("c", "Key not found"))
Key not found
Output
123 marks
Final value of my_list after the following lines would be:
own_list = [1, 2, 3]:
own_list.append(4, 5)
Solution

The code will not run, so the list never gets a final value:

  • Line 1 ends with a colon, which is a SyntaxError; Python stops before running anything.
  • Line 2: \texttt{append()} takes exactly one argument, so \texttt{append(4, 5)} is a TypeError.
  • The question names my_list, but the code uses own_list.
  File "prog.py", line 1
    own_list = [1, 2, 3]:
                        ^
SyntaxError: invalid syntax
Output as printed

If the colon is removed and the two numbers are added with \texttt{extend([4, 5])} (or appended one at a time), the final value is [1, 2, 3, 4, 5]. With \texttt{append((4, 5))} the tuple is added as one item: [1, 2, 3, (4, 5)].

own_list = [1, 2, 3]
own_list.extend([4, 5])
print(own_list)
[1, 2, 3, 4, 5]
Output

Extended response questions (ERQs)p. 52–53

18 marks
Write a Python function that calculates the factorial of a given number. Test the function with 5 different inputs.
Solution

Idea

The factorial of a whole number n is n!=1\times2\times3\times\dots\times n, with 0!=1. It is not defined for negative numbers. A loop multiplies the numbers from 2 to n; the same result can be written recursively as n!=n\times(n-1)!.

Program

def factorial(n):
    """Return n! for a whole number n (n >= 0)."""
    if n < 0:
        return "Not defined for negative numbers"
    result = 1
    for i in range(2, n + 1):     # multiply 2 x 3 x ... x n
        result = result * i
    return result

def factorial_recursive(n):       # the same using recursion
    if n == 0:
        return 1
    return n * factorial_recursive(n - 1)

for number in [0, 1, 5, 7, 10]:   # test with 5 different inputs
    print(f"{number}! = {factorial(number)} | recursive: {factorial_recursive(number)}")
print("-3! =", factorial(-3))
Factorial function tested with 5 inputs
0! = 1 | recursive: 1
1! = 1 | recursive: 1
5! = 120 | recursive: 120
7! = 5040 | recursive: 5040
10! = 3628800 | recursive: 3628800
-3! = Not defined for negative numbers
Output

Explanation

  • Lines 3 and 4 reject negative input.
  • Lines 5 to 7: \texttt{result} starts at 1 and is multiplied by every number from 2 to n. For n=0 or 1 the loop does not run, so the answer is 1.
  • Lines 10 to 13: the recursive version calls itself with n-1 until it reaches the base case n=0.
  • Lines 15 and 16 test both functions with the 5 inputs 0, 1, 5, 7 and 10; both give the same answers, which checks each other. Line 17 tests the invalid input.
Trace table for factorial(5)
iresult = result * i
21 × 2 = 2
32 × 3 = 6
46 × 4 = 24
524 × 5 = 120
28 marks
Implement a Python class 'Rectangle' with methods to calculate the area and perimeter of a rectangle. Create instances of the class and print the perimeter of rectangle.
Solution

Program

class Rectangle:
    def __init__(self, length, width):     # constructor stores the two sides
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * (self.length + self.width)

r1 = Rectangle(5, 3)                       # instances (objects) of the class
r2 = Rectangle(10, 4.5)
r3 = Rectangle(7, 7)

for name, r in [("r1", r1), ("r2", r2), ("r3", r3)]:
    print(f"{name}: {r.length} x {r.width}  area = {r.area()}  perimeter = {r.perimeter()}")
Rectangle class with three objects
r1: 5 x 3  area = 15  perimeter = 16
r2: 10 x 4.5  area = 45.0  perimeter = 29.0
r3: 7 x 7  area = 49  perimeter = 28
Output

Explanation

  • Line 1: \texttt{class Rectangle} is the template. Its attributes are \texttt{length} and \texttt{width}.
  • Lines 2 to 4: the constructor \texttt{\_\_init\_\_} runs when an object is created and stores the two sides in \texttt{self}, the object itself.
  • Lines 6 and 7: \texttt{area()} returns length × width. Lines 9 and 10: \texttt{perimeter()} returns 2(length + width).
  • Lines 12 to 14 create three instances; each has its own length and width but shares the methods.
  • Lines 16 and 17 call the methods for every object. For r1: area =5\times3=15 and perimeter =2(5+3)=16.
38 marks
Write a Python program highlighting the use of functions to simplify a problem. For example, create a program that calculates the average of a list of numbers and by using separate functions to add all numbers, marking repeated values and calculate the average of the list with and without repeating values.
Solution

How functions simplify the problem

The problem is split into four small jobs, each done by one function: add all numbers, find repeated values, remove repeats, and find the average. Each function is short and easy to test, and \texttt{average()} reuses \texttt{add\_all()} instead of repeating the loop. This is complexity reduction through functions.

Program

def add_all(numbers):                  # adds every number
    total = 0
    for n in numbers:
        total += n
    return total

def mark_repeated(numbers):            # finds values that appear more than once
    repeated = []
    for n in numbers:
        if numbers.count(n) > 1 and n not in repeated:
            repeated.append(n)
    return repeated

def remove_repeats(numbers):           # keeps only the first copy of each value
    unique = []
    for n in numbers:
        if n not in unique:
            unique.append(n)
    return unique

def average(numbers):                  # reuses add_all()
    return add_all(numbers) / len(numbers)

data = [4, 7, 4, 9, 7, 5, 10, 4]
print("List:", data)
print("Sum of all numbers:", add_all(data))
print("Repeated values:", mark_repeated(data))
print("Average with repeating values:", round(average(data), 2))
unique = remove_repeats(data)
print("List without repeats:", unique)
print("Average without repeating values:", round(average(unique), 2))
Average with and without repeated values
List: [4, 7, 4, 9, 7, 5, 10, 4]
Sum of all numbers: 50
Repeated values: [4, 7]
Average with repeating values: 6.25
List without repeats: [4, 7, 9, 5, 10]
Average without repeating values: 7.0
Output

Working

  • The list has 8 values with sum 4+7+4+9+7+5+10+4=50, so the average with repeats is 50/8=6.25.
  • 4 and 7 appear more than once, so they are marked as repeated.
  • Without repeats the list is [4, 7, 9, 5, 10]: sum 35, average 35/5=7.0.
48 marks
Create a Python dictionary to store student names as keys and their grades as values. Write a program to add new students, update existing students' grades, and remove students from the dictionary.
Solution

Program

grades = {"Ali": "B", "Sana": "A", "Hamza": "C"}

def show():
    for name, grade in grades.items():
        print(f"  {name}: {grade}")

while True:
    choice = input("1-Add 2-Update 3-Remove 4-Show 5-Exit: ")
    if choice == "1":
        name = input("Name: ")
        if name in grades:
            print(name, "already exists")
        else:
            grades[name] = input("Grade: ")          # new key-value pair
            print(name, "added")
    elif choice == "2":
        name = input("Name: ")
        if name in grades:
            grades[name] = input("New grade: ")      # existing key: value replaced
            print(name, "updated")
        else:
            print(name, "not found")
    elif choice == "3":
        name = input("Name: ")
        if name in grades:
            del grades[name]                         # remove key and value
            print(name, "removed")
        else:
            print(name, "not found")
    elif choice == "4":
        show()
    elif choice == "5":
        break
Menu-driven program on a dictionary
1-Add 2-Update 3-Remove 4-Show 5-Exit: 4
  Ali: B
  Sana: A
  Hamza: C
1-Add 2-Update 3-Remove 4-Show 5-Exit: 1
Name: Zara
Grade: A
Zara added
1-Add 2-Update 3-Remove 4-Show 5-Exit: 2
Name: Ali
New grade: A
Ali updated
1-Add 2-Update 3-Remove 4-Show 5-Exit: 3
Name: Hamza
Hamza removed
1-Add 2-Update 3-Remove 4-Show 5-Exit: 3
Name: Omar
Omar not found
1-Add 2-Update 3-Remove 4-Show 5-Exit: 4
  Ali: A
  Sana: A
  Zara: A
1-Add 2-Update 3-Remove 4-Show 5-Exit: 5
Sample run

Explanation

  • Line 1 creates the dictionary: names are keys, grades are values.
  • The while loop shows a menu until the user enters 5 (\texttt{break}).
  • Add: \texttt{grades[name] = grade} with a new key creates a new pair. The program first checks with \texttt{in} so an existing student is not overwritten by mistake.
  • Update: the same assignment with an existing key replaces the old value (Ali's B becomes A).
  • Remove: \texttt{del grades[name]} deletes the key and its value; the \texttt{in} check prevents a KeyError for a name that is not present (Omar).
  • Show: \texttt{items()} gives every name-grade pair.
58 marks
Write a Python program to create a file named data.txt, write a string to it and then read the content of the file and display the output.
Solution

Program

# write mode creates data.txt (or empties it) and writes the string
with open("data.txt", "w") as f:
    f.write("Python makes file handling easy.\nThis is the second line.")
print("data.txt created and written.")

# read mode opens the same file and reads the whole content
with open("data.txt", "r") as f:
    content = f.read()
print("Content of data.txt:")
print(content)
Write, then read data.txt
data.txt created and written.
Content of data.txt:
Python makes file handling easy.
This is the second line.
Output

Explanation

  • Line 2: \texttt{open("data.txt", "w")} creates data.txt in the current folder (or empties it if it exists). \texttt{with} closes it automatically.
  • Line 3: \texttt{write()} stores the string. \texttt{\textbackslash n} is the end-of-line marker, so the file has two lines.
  • Line 7 opens the same file in read mode; line 8: \texttt{read()} returns the whole content as one string.
  • Lines 9 and 10 display the content.
68 marks
Create a Python program that appends a new line of text to an existing file. If the file does not exist, it should create a new file.
Solution

Idea

Append mode "a" does both jobs: it adds text at the end of an existing file without erasing it, and it creates the file if it does not exist. The program also reports which case happened using \texttt{os.path.exists()}.

Program

import os

def add_line(filename, text):
    if os.path.exists(filename):
        print(f"{filename} exists: appending a new line.")
    else:
        print(f"{filename} does not exist: creating it.")
    with open(filename, "a") as f:       # "a" appends, and creates the file if missing
        f.write(text + "\n")

add_line("diary.txt", "Monday: revised lists")        # first call: file is created
add_line("diary.txt", "Tuesday: revised files")       # second call: line is appended

with open("diary.txt") as f:
    print(f.read(), end="")
Appending with mode "a"
diary.txt does not exist: creating it.
diary.txt exists: appending a new line.
Monday: revised lists
Tuesday: revised files
Output

Explanation

  • Line 4 checks whether the file is already on the disk.
  • Line 8: mode "a" opens the file for appending (creating it the first time). Line 9 writes the text plus \texttt{\textbackslash n} so each entry is on its own line.
  • The first call creates diary.txt; the second finds it and appends. Reading the file shows both lines, proving the old line was kept.
  • If mode "w" were used instead, the second call would erase Monday's line.
78 marks
Write Python code using Pygame that creates a simple window with a button. When the button is clicked, it should display a message on the window.
Solution

Program

import pygame

pygame.init()
screen = pygame.display.set_mode((400, 300))           # the window
pygame.display.set_caption("Button Demo")
font = pygame.font.SysFont(None, 32)
button = pygame.Rect(140, 110, 120, 50)                 # x, y, width, height
message = ""

running = True
while running:                                          # main loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:                   # window closed
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN and button.collidepoint(event.pos):
            message = "Button clicked!"                 # click inside the button

    screen.fill((255, 255, 255))                        # white background
    pygame.draw.rect(screen, (0, 120, 215), button)     # blue button
    screen.blit(font.render("Click me", True, (255, 255, 255)), (button.x + 14, button.y + 14))
    if message:
        screen.blit(font.render(message, True, (0, 0, 0)), (115, 200))
    pygame.display.flip()                               # show the new frame

pygame.quit()
Pygame window with a clickable button (install first with pip install pygame)

Output

A 400 × 300 window titled "Button Demo" opens with a blue button labelled "Click me". When the user clicks inside the button, the text "Button clicked!" appears below it. Closing the window ends the program. (The program was tested by sending a mouse click at the button's centre: the message became "Button clicked!".)

Explanation

  • Lines 3 to 6: initialise Pygame, create the window, set its title and choose a font.
  • Line 7: \texttt{pygame.Rect(140, 110, 120, 50)} is the button's position and size.
  • Lines 11 to 16: the main loop reads events. QUIT ends the loop; a MOUSEBUTTONDOWN event whose position lies inside the rectangle (\texttt{collidepoint}) sets the message.
  • Lines 18 to 22 draw the background, the button, its label and the message; \texttt{render()} turns text into an image and \texttt{blit()} places it.
  • Line 23: \texttt{flip()} shows the new frame. Line 25 closes Pygame.
88 marks
Develop a Python program that calculates and prints the transpose of a 3x3 matrix represented using a nested list.
Solution

Idea

The transpose swaps rows and columns: element (r, c) moves to (c, r). So column c of the matrix becomes row c of the transpose.

Program

def read_matrix():
    return [[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]]

def transpose(m):
    result = []
    for col in range(len(m[0])):          # each column of m ...
        new_row = []
        for row in range(len(m)):
            new_row.append(m[row][col])   # ... becomes a row of the result
        result.append(new_row)
    return result

def show(m):
    for row in m:
        print("  ".join(str(x) for x in row))

A = read_matrix()
print("Matrix:")
show(A)
print("Transpose:")
show(transpose(A))
Transpose of a 3 × 3 matrix
Matrix:
1  2  3
4  5  6
7  8  9
Transpose:
1  4  7
2  5  8
3  6  9
Output

Explanation

  • Lines 1 to 4: the matrix is a nested list; each inner list is a row.
  • Lines 6 to 13: the outer loop takes each column index; the inner loop collects \texttt{m[row][col]} from every row, building one new row which is appended to the result.
  • Lines 15 to 17 print each row with the numbers separated by spaces.
  • Check: the first column of A is 1, 4, 7, which is the first row of the transpose.
Trace table
colvalues collected (row = 0, 1, 2)new row
0m[0][0], m[1][0], m[2][0][1, 4, 7]
1m[0][1], m[1][1], m[2][1][2, 5, 8]
2m[0][2], m[1][2], m[2][2][3, 6, 9]
98 marks
Write a Python function that counts the number of occurrences of each letter in a text file and prints the results.
Solution

Idea

Read the whole file, convert it to lower case so that 'P' and 'p' are counted together, and keep a dictionary in which each letter is a key and its count is the value.

Program

def count_letters(filename):
    counts = {}
    try:
        with open(filename, "r") as f:
            for ch in f.read().lower():        # count 'A' and 'a' together
                if ch.isalpha():               # letters only
                    counts[ch] = counts.get(ch, 0) + 1
    except FileNotFoundError:
        print("File not found:", filename)
    return counts

result = count_letters("story.txt")
for letter in sorted(result):
    print(letter, ":", result[letter])
Counting letters in story.txt
a : 6
b : 1
c : 1
e : 1
f : 1
g : 1
h : 3
i : 6
k : 1
l : 1
m : 1
n : 4
o : 2
p : 1
r : 1
s : 4
t : 5
u : 4
y : 1
Output (story.txt contains: "Pakistan is a beautiful country." and "It has high mountains.")

Explanation

  • Line 4 opens the file in read mode inside try, so a missing file gives a message instead of a crash.
  • Line 5 reads the text and converts it to lower case; the loop takes one character at a time.
  • Line 6: \texttt{isalpha()} skips spaces, full stops and new-line characters.
  • Line 7: \texttt{counts.get(ch, 0)} returns the current count (0 the first time) and 1 is added.
  • Lines 12 to 14 print the letters in alphabetical order using \texttt{sorted()}.
108 marks
Write a Python program to connect to a database and demonstrate how to insert, update and retrieve data from the database. Explain the importance of database connectivity in Python applications.
Solution

Program

import sqlite3

conn = sqlite3.connect("shop.db")                  # connect (creates shop.db if needed)
cur = conn.cursor()
cur.execute("DROP TABLE IF EXISTS Laptop")
cur.execute("""CREATE TABLE Laptop (
                   L_ID TEXT PRIMARY KEY,
                   L_Name TEXT NOT NULL,
                   L_Price INTEGER NOT NULL)""")

# INSERT: add records
laptops = [("L1", "Dell XPS 13", 100000), ("L2", "MacBook Air", 90000), ("L3", "HP Spectre x360", 120000)]
cur.executemany("INSERT INTO Laptop VALUES (?, ?, ?)", laptops)
conn.commit()

def show(title):
    print(title)
    cur.execute("SELECT * FROM Laptop ORDER BY L_ID")      # RETRIEVE
    for row in cur.fetchall():
        print("  ", row)

show("After insert:")
# UPDATE: change the price of one laptop
cur.execute("UPDATE Laptop SET L_Price = ? WHERE L_ID = ?", (95000, "L2"))
conn.commit()
show("After update:")

cur.execute("SELECT L_Name FROM Laptop WHERE L_Price > ?", (95000,))
print("Laptops above 95000:", [r[0] for r in cur.fetchall()])
conn.close()
Insert, update and retrieve with sqlite3
After insert:
   ('L1', 'Dell XPS 13', 100000)
   ('L2', 'MacBook Air', 90000)
   ('L3', 'HP Spectre x360', 120000)
After update:
   ('L1', 'Dell XPS 13', 100000)
   ('L2', 'MacBook Air', 95000)
   ('L3', 'HP Spectre x360', 120000)
Laptops above 95000: ['Dell XPS 13', 'HP Spectre x360']
Output

Explanation

  • Line 3: \texttt{sqlite3.connect()} connects to the database file (created if it does not exist). Line 4 makes a cursor that executes SQL.
  • Line 5 removes any old copy of the table and lines 6 to 9 create it. Lines 12 and 13 insert three records; \texttt{?} placeholders pass the values safely. \texttt{commit()} saves them permanently.
  • Line 18: \texttt{SELECT} with \texttt{fetchall()} retrieves the rows.
  • Line 24 updates the price of L2 with \texttt{UPDATE ... SET ... WHERE}.
  • Line 28 retrieves only the rows that satisfy a condition. Line 30 closes the connection.

Importance of database connectivity

  • Permanent storage: data survives after the program ends, unlike lists and dictionaries in RAM.
  • Large data: databases handle thousands of records efficiently and search them quickly with SQL.
  • Sharing and concurrency: many users and applications can use the same data at the same time.
  • Integrity and security: keys, constraints (such as NOT NULL) and access control keep data correct and safe.
  • Real applications: login systems, online shops, school and bank systems all store their data through such connections.
118 marks
Explain unit testing and its importance in Python programming. How does it help in catching errors early during the development process? Include an example of a unit test for a simple Python function.
Solution

Unit testing

Unit testing is testing the smallest parts (units) of a program, such as single functions or methods, in isolation. For each unit we write test cases that call it with chosen inputs and compare the actual result with the expected result using assertions. Python's built-in unittest framework groups test cases in a class derived from \texttt{unittest.TestCase} and reports which pass and which fail.

Importance and catching errors early

  • Each function is checked as soon as it is written, so a bug is found while the code is small and fresh in the programmer's mind; it is much cheaper to fix than after the whole program is built.
  • A failing test points to the exact function and input that is wrong, which narrows down debugging.
  • Tests are saved and re-run automatically after every change (regression testing), so a new change that breaks old code is noticed immediately.
  • Tests of boundary values (such as 40, 60, 80 marks) catch off-by-one mistakes that manual testing misses.
  • Tests also document how a function is supposed to behave.

Example

import unittest

def calculate_grade(marks):
    if marks < 0 or marks > 100:
        return "Invalid"
    if marks >= 80:
        return "A"
    if marks >= 60:
        return "B"
    if marks >= 40:
        return "C"
    return "F"

class TestGrade(unittest.TestCase):
    def test_a_grade(self):
        self.assertEqual(calculate_grade(85), "A")
    def test_boundaries(self):
        self.assertEqual(calculate_grade(80), "A")
        self.assertEqual(calculate_grade(60), "B")
        self.assertEqual(calculate_grade(40), "C")
    def test_fail(self):
        self.assertEqual(calculate_grade(39), "F")
    def test_invalid(self):
        self.assertEqual(calculate_grade(105), "Invalid")

unittest.main(verbosity=2)
Unit tests for a grade function
test_a_grade (__main__.TestGrade.test_a_grade) ... ok
test_boundaries (__main__.TestGrade.test_boundaries) ... ok
test_fail (__main__.TestGrade.test_fail) ... ok
test_invalid (__main__.TestGrade.test_invalid) ... ok

----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK
Output (the time may differ slightly)

If someone later changes \texttt{marks >= 80} to \texttt{marks > 80} by mistake, test_boundaries fails at once and reports the wrong value, before the error reaches users.

128 marks
Write a Python code that selects a number between 1 and 10 and the player will try to guess the number in 5 tries. For every incorrect guess, the program provides a hint whether the last guess was on higher or lower side. If the number is successfully guessed then a Congratulating message should be displayed along with in how many attempts the number was guessed correctly. Otherwise another message should be displayed along with the chosen number.
Solution

Program

import random

name = input("Hello, What's your name? ")
number = random.randint(1, 10)              # secret number from 1 to 10
print(f"Okay {name}, I am guessing a number between 1 and 10:")

guessed = False
for attempt in range(1, 6):                 # at most 5 tries
    guess = int(input(f"Try {attempt}: "))
    if guess < number:
        print("Your guess is too low")
    elif guess > number:
        print("Your guess is too high")
    else:
        guessed = True
        break

if guessed:
    print(f"Congratulations {name}! You guessed the number in {attempt} tries!")
else:
    print(f"You did not guess the number. The number was {number}")
Number guessing game
Hello, What's your name? Abrar
Okay Abrar, I am guessing a number between 1 and 10:
Try 1: 5
Your guess is too low
Try 2: 8
Your guess is too high
Try 3: 7
Congratulations Abrar! You guessed the number in 3 tries!
Sample run 1 (the secret number happened to be 7)
Hello, What's your name? Azam
Okay Azam, I am guessing a number between 1 and 10:
Try 1: 2
Your guess is too low
Try 2: 4
Your guess is too low
Try 3: 6
Your guess is too low
Try 4: 8
Your guess is too low
Try 5: 10
Your guess is too high
You did not guess the number. The number was 9
Sample run 2 (the secret number happened to be 9)

Explanation

  • Line 4: \texttt{random.randint(1, 10)} chooses the secret number; each run is different.
  • Line 8: \texttt{for attempt in range(1, 6)} allows at most 5 tries and counts them.
  • Lines 10 to 16 give the hint: too low, too high, or correct. On a correct guess \texttt{guessed} becomes True and \texttt{break} leaves the loop early.
  • Lines 18 to 21: after the loop, the congratulation message shows the number of attempts; otherwise the secret number is revealed.
138 marks
The following code is supposed to open a file 'abc.txt' and count the number of occurrences of the character 's' and display output like: The letter 's' appears 3 times in the file 'abc.txt'. (Sample text in file 'abc.txt': I scream, you scream, we all scream for ice cream.) However, the code does not execute. Debug and remove any bug(s) from the code.
file_name = 'abc.txt'
target_letter = 'S'
try:
    file = open(file_name, "r")
    text = file.read()
    file.close
except FileNotFoundError:
    print(f"Error: File '{file_name}' not found.")
    exit()
count = 0
for char in text:
    if char = target_letter:
        count += 1
print("The letter '{target_letter}' appears " + count + " times in the file '{file_name}'.")
Solution

Bugs found

Bugs in the given code
LineBugCorrection
2target_letter is 'S' (capital), but the text has lower-case s; the count would be 0.\texttt{target\_letter = 's'}
6\texttt{file.close} without brackets does not call the method, so the file is never closed.\texttt{file.close()}
12\texttt{if char = target\_letter:} uses the assignment operator; this is a SyntaxError, which is why the code does not execute.\texttt{if char == target\_letter:}
14A string and the integer count are joined with +, which is a TypeError; the {target_letter} and {file_name} placeholders are not in an f-string, so they would be printed literally.use an f-string with {count}
  File "prog.py", line 12
    if char = target_letter:
       ^^^^^^^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?
Output of the given code

Corrected program

file_name = 'abc.txt'
target_letter = 's'                          # 1. lower-case s, as in the sample output
try:
    file = open(file_name, "r")
    text = file.read()
    file.close()                             # 2. brackets added: the method is now called
except FileNotFoundError:
    print(f"Error: File '{file_name}' not found.")
    exit()
count = 0
for char in text:
    if char == target_letter:                # 3. == compares, = assigns
        count += 1
print(f"The letter '{target_letter}' appears {count} times in the file '{file_name}'.")   # 4. f-string
Debugged code
The letter 's' appears 3 times in the file 'abc.txt'.
Output

The word "scream" appears 3 times and each contains one 's'; no other word has an 's', so the count is 3, as in the sample output.

Mini projectsp. 52

Open-ended programming projects at the end of the unit. Pygame and tkinter programs open a window, so their result is described in words.

1
Mini Project 1: Design a game where multiple objects are falling from the top. The objective is to "Catch The Falling Objects" using arrow keys. For every catch, a score increases by 1 point. The game ends whenever an object is missed.
Solution

Plan

  • A basket (rectangle) at the bottom moves left and right with the arrow keys.
  • Three objects start at different heights above the window and fall at a fixed speed.
  • If an object touches the basket, the score increases by 1 and that object restarts from the top at a random x position.
  • If an object passes the bottom of the window, the game is over.

Program

import pygame
import random

pygame.init()
WIDTH, HEIGHT = 480, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Catch The Falling Objects")
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 36)

basket = pygame.Rect(WIDTH // 2 - 50, HEIGHT - 30, 100, 20)
objects = []                                   # several falling objects
for i in range(3):
    objects.append(pygame.Rect(random.randint(0, WIDTH - 20), -i * 200, 20, 20))
speed = 4
score = 0
game_over = False
running = True

while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    if not game_over:
        keys = pygame.key.get_pressed()        # arrow keys move the basket
        if keys[pygame.K_LEFT] and basket.left > 0:
            basket.x -= 8
        if keys[pygame.K_RIGHT] and basket.right < WIDTH:
            basket.x += 8

        for obj in objects:
            obj.y += speed
            if obj.colliderect(basket):        # caught: 1 point, restart at the top
                score += 1
                obj.x = random.randint(0, WIDTH - 20)
                obj.y = -20
            elif obj.top > HEIGHT:             # missed: game ends
                game_over = True

    screen.fill((255, 255, 255))
    pygame.draw.rect(screen, (0, 120, 215), basket)
    for obj in objects:
        pygame.draw.ellipse(screen, (220, 50, 50), obj)
    screen.blit(font.render(f"Score: {score}", True, (0, 0, 0)), (10, 10))
    if game_over:
        screen.blit(font.render("Game Over", True, (0, 0, 0)), (WIDTH // 2 - 60, HEIGHT // 2))
    pygame.display.flip()
    clock.tick(60)                             # 60 frames per second

pygame.quit()
Catch The Falling Objects (Pygame)

Output and explanation

A 480 × 600 window shows red balls falling towards a blue basket, with "Score: n" at the top left. \texttt{colliderect()} detects a catch; \texttt{obj.top > HEIGHT} detects a miss and shows "Game Over". \texttt{clock.tick(60)} keeps the speed the same on every computer. The program was run with a test that let the objects fall without moving the basket: the game correctly ended with Game Over once an object was missed.

2
Mini Project 2: Design two textboxes such that the first is for entering text while the second shows the text converted to uppercase. The user can click on the first textbox and type a sentence. As the user types, the second textbox automatically displays the uppercase version. The second textbox is not editable.
Solution

Plan

A \texttt{StringVar} is linked to the first Entry. \texttt{trace\_add("write", ...)} calls a function every time its text changes, that is, on every key press. The function copies the text in upper case into the second Entry, which is kept in the "readonly" state so the user cannot type in it.

Program

import tkinter as tk

def update_upper(*args):
    output.config(state="normal")              # unlock to change the text
    output.delete(0, tk.END)
    output.insert(0, text_var.get().upper())   # copy the text in upper case
    output.config(state="readonly")            # lock again: user cannot edit

root = tk.Tk()
root.title("Uppercase Converter")

tk.Label(root, text="Type here:").grid(row=0, column=0, padx=5, pady=5)
text_var = tk.StringVar()
text_var.trace_add("write", update_upper)      # run update_upper on every key press
entry = tk.Entry(root, textvariable=text_var, width=30)
entry.grid(row=0, column=1, padx=5, pady=5)

tk.Label(root, text="Uppercase:").grid(row=1, column=0, padx=5, pady=5)
output = tk.Entry(root, width=30, state="readonly")
output.grid(row=1, column=1, padx=5, pady=5)

root.mainloop()
Live uppercase converter (tkinter)

Output and explanation

The window "Uppercase Converter" has two labelled boxes. Typing "hello world" in the first box immediately shows "HELLO WORLD" in the second. The second box must be switched to "normal" for a moment so the program itself can change it, then back to "readonly". The program was tested by typing text into the first box automatically; the second box showed the uppercase text and did not accept typing.

Important exam questions (not in the exercise)

Likely FBISE short and long questions from this unit that are not in the book's exercise, with full answers and tested programs.

13 marks
Differentiate between a list and a tuple in Python with an example of each.
Solution
ListTuple
Written in square brackets: \texttt{[1, 2]}Written in round brackets: \texttt{(1, 2)}
Mutable: items can be added, changed or removedImmutable: cannot be changed after it is created
Has methods append(), insert(), remove(), pop()Has only count() and index()
Used for data that changes, such as a list of marksUsed for fixed data, such as days of the week or coordinates

Example: \texttt{marks = [85, 92]} then \texttt{marks.append(78)} works; \texttt{days = ('Mon', 'Tue')} then \texttt{days[0] = 'Sun'} gives a TypeError.

23 marks
What is a set in Python? What happens to duplicate values in a set? Give an example.
Solution

A set is a collection of unique, unordered elements written in curly brackets. It has no index. Duplicate values are removed automatically: only one copy of each value is kept even if it is added many times. Sets are used for unique data such as usernames and for fast membership tests with \texttt{in}.

s = {5, 3, 5, 3, 8}
s.add(8)
print(s, len(s))
{8, 3, 5} 3
Output
33 marks
Differentiate between insert() and append() methods of a list.
Solution

Both add an element to a list, but at different places:

  • \texttt{append(x)} takes one argument and adds x at the end of the list.
  • \texttt{insert(i, x)} takes two arguments and puts x at index i; the later elements move one place right. If i is larger than the length, x goes at the end.
a = [10, 20, 30]
a.append(40)
a.insert(1, 15)
print(a)
[10, 15, 20, 30, 40]
Output
43 marks
Differentiate between remove() and pop() methods of a list.
Solution
  • \texttt{remove(x)} deletes the first element whose value is x. It returns nothing and raises a ValueError if x is not in the list.
  • \texttt{pop(i)} deletes the element at index i and returns it, so the removed value can be used. Without an index it removes the last element. An invalid index raises an IndexError.

Use remove() when the value is known and pop() when the position is known.

g = [85, 92, 78, 95]
g.remove(78)
last = g.pop()
print(g, last)
[85, 92] 95
Output
53 marks
Write the output of the following code.
nums = [10, 20, 30, 40, 50]
print(nums[1:4])
print(nums[-2])
nums.insert(1, 15)
nums.pop()
print(nums, len(nums))
Solution
[20, 30, 40]
40
[10, 15, 20, 30, 40] 5
Output
  • \texttt{nums[1:4]} takes indexes 1, 2 and 3 (the slice stops before 4): [20, 30, 40].
  • \texttt{nums[-2]} is the second element from the end: 40.
  • \texttt{insert(1, 15)} gives [10, 15, 20, 30, 40, 50]; \texttt{pop()} removes the last element 50, leaving 5 elements.
63 marks
Why must dictionary keys be immutable? Why is searching a dictionary faster than searching a list?
Solution

Python stores each key at a position calculated from the key itself (its hash). If a key could change after being stored, its position would no longer match and the value could never be found again. So keys must be unchangeable types such as strings, numbers or tuples; a list cannot be a key.

For the same reason, searching a dictionary is fast: Python calculates the key's position and jumps straight to it, so the time hardly grows with the size. Searching a list checks the items one by one (linear search), so a longer list takes longer.

73 marks
What is encapsulation? Give an example.
Solution

Encapsulation is information hiding: the data (attributes) and the methods that work on that data are bundled together in one unit, the class, and direct access to the data is restricted. The data can be read or changed only through the object's methods, which protects it from wrong or unwanted changes and hides the internal working.

Example: in a BankAccount class the balance is private (\texttt{self.\_\_balance}). It cannot be set directly; \texttt{deposit()} adds money, \texttt{withdraw()} subtracts it only if the balance is enough, and \texttt{get\_balance()} only reads it.

83 marks
What is polymorphism? Explain with an example.
Solution

Polymorphism means "many forms". Objects of different classes are treated as objects of a common type, and the same method call behaves differently depending on the class of the object. It allows general code that works with many kinds of objects and makes behaviour easy to extend.

Example: Triangle, Rectangle and Circle are all Shapes and each has an \texttt{area()} method. In \texttt{for s in shapes: print(s.area())} the triangle uses \frac12bh, the rectangle l\times w and the circle \pi r^2, although the instruction is the same.

93 marks
Differentiate between abstraction and encapsulation.
Solution
AbstractionEncapsulation
Shows only the essential features and hides the implementation details.Bundles data with the methods that use it and restricts direct access to the data.
Deals with what an object does.Deals with how the data is protected.
Achieved by designing simple method interfaces (start(), stop()).Achieved with private attributes and getter/setter methods.
Example: a driver uses accelerate() without knowing how the engine burns fuel.Example: a balance changed only through deposit() and withdraw().
103 marks
Explain the read(), readline(), write() and close() file methods.
Solution
  • \texttt{read()}: reads the whole file (from the current position) and returns it as one string.
  • \texttt{readline()}: reads one line, including its end-of-line character; \texttt{readlines()} returns all lines as a list.
  • \texttt{write(s)}: writes the string s to a file opened in "w" or "a" mode and returns the number of characters written.
  • \texttt{close()}: closes the file, making sure all data is saved and the memory is released.
f = open("abc.txt", "r")
first = f.readline()
rest = f.read()
f.close()
113 marks
What is the difference between the write mode "w" and the append mode "a"?
Solution
  • "w" (write): creates the file if it does not exist; if it exists, its old contents are erased first and the new data is written from the beginning.
  • "a" (append): creates the file if it does not exist; if it exists, the old contents are kept and new data is added at the end.

So "w" is used to make a fresh file (for example a new report) and "a" to add records to an existing file (for example a log or attendance file). Opening an important file in "w" mode by mistake destroys its data.

123 marks
Why is a try-except block used in file handling? Give an example.
Solution

File operations can fail for reasons outside the program: the file may not exist, the user may not have permission, or the disk may give a read or write error. Such an error (exception) would end the program abnormally. A try block contains the risky file code, and except blocks catch particular errors (FileNotFoundError, PermissionError, IOError) and handle them, for example by printing a clear message, so the program continues or ends normally.

try:
    with open("marks.txt") as f:
        print(f.read())
except FileNotFoundError:
    print("marks.txt not found")
marks.txt not found
Output when the file is missing
133 marks
Define candidate key, foreign key and composite key with examples.
Solution
  • Candidate key: any attribute or set of attributes that can uniquely identify a record. One candidate key is chosen as the primary key; the others are secondary keys. Example: RollNo and CNIC in a Student table.
  • Foreign key: a field in one table that refers to the primary key of another table and links the two. Example: C_ID in the Order table refers to C_ID in the Customer table.
  • Composite key: a key made of two or more attributes that together identify a row uniquely. Example: (OrderID, L_ID) in an order table where one order contains several laptops.
143 marks
Differentiate between relational and NoSQL databases.
Solution
Relational databaseNoSQL database
Stores data in tables of rows and columns.Stores data as documents, key-value pairs, graphs and so on.
Fixed structure (schema) that must be defined first.Flexible structure; no fixed table layout.
Tables are linked by primary and foreign keys; queried with SQL.Relations are less strict; each product has its own query method.
Best for structured data such as banking and school records. Examples: MySQL, SQLite.Best for huge unstructured or semi-structured data such as social media posts. Examples: MongoDB, Redis.
153 marks
Write SQL queries for a table Customer(C_ID, C_Name, C_Address, C_Phone) to: (a) show all customers sorted by name in descending order, (b) change the phone number of customer C3, (c) delete customer C2.
Solution
SELECT * FROM Customer ORDER BY C_Name DESC;
(a) sorted list
UPDATE Customer SET C_Phone = '555-0000' WHERE C_ID = 'C3';
(b) update one record
DELETE FROM Customer WHERE C_ID = 'C2';
(c) delete one record

In (b) and (c) the WHERE clause is essential: without it every customer's phone would be changed, or every record deleted. ORDER BY sorts in ascending order unless DESC is written.

163 marks
Differentiate between a breakpoint and a watchpoint in debugging.
Solution
BreakpointWatchpoint (watch)
Placed on a line of code.Set on a variable, expression or formula.
The program pauses just before that line in debug mode.Its current value is shown at every pause, and a change or wrong value is flagged.
Answers: has the program reached this point, and in what state?Answers: what is the value of this variable now?
Example: a breakpoint on line 12 of the test function.Example: watching result, which showed 0 when 2 was expected.

Both are tools of an IDE's debugger and are used together to locate logical errors without adding print statements.

18 marks
Write a Python program that reads the marks of n students into a list and displays the highest, lowest and average marks, and the number of students who scored above the average. Use functions.
Solution

Program

def read_marks(n):
    marks = []
    for i in range(1, n + 1):
        marks.append(int(input(f"Marks of student {i}: ")))
    return marks

def report(marks):
    avg = sum(marks) / len(marks)
    above = [m for m in marks if m > avg]
    print("Highest:", max(marks), "| Lowest:", min(marks))
    print("Average:", round(avg, 2))
    print("Students above average:", len(above))

n = int(input("How many students? "))
report(read_marks(n))
Marks analysis using a list and functions
How many students? 5
Marks of student 1: 67
Marks of student 2: 82
Marks of student 3: 45
Marks of student 4: 90
Marks of student 5: 71
Highest: 90 | Lowest: 45
Average: 71.0
Students above average: 2
Sample run

Explanation

  • \texttt{read\_marks(n)} repeats n times, converts each input to int and appends it to the list.
  • \texttt{report()} uses the built-in functions \texttt{sum()}, \texttt{len()}, \texttt{max()} and \texttt{min()}.
  • The list comprehension \texttt{[m for m in marks if m > avg]} keeps only the marks above the average.
  • Check: total =67+82+45+90+71=355, average =355/5=71.0; 82 and 90 are above it, so the count is 2 (71 is not above 71).
28 marks
Write a Python program that adds two 3 × 3 matrices stored as nested lists and prints the result in matrix form. Also print the sum of the main diagonal of the first matrix.
Solution

Program

A = [[1, 2, 3],
     [4, 5, 6],
     [7, 8, 9]]
B = [[9, 8, 7],
     [6, 5, 4],
     [3, 2, 1]]

C = []                                   # result matrix
for r in range(3):
    row = []
    for c in range(3):
        row.append(A[r][c] + B[r][c])    # add elements in the same position
    C.append(row)

print("Sum of the matrices:")
for row in C:
    for value in row:
        print(value, end="\t")
    print()

total = sum(A[i][i] for i in range(3))   # main diagonal: (0,0), (1,1), (2,2)
print("Sum of main diagonal of A:", total)
Matrix addition with nested lists
Sum of the matrices:
10	10	10	
10	10	10	
10	10	10	
Sum of main diagonal of A: 15
Output

Explanation

  • Two matrices are added element by element: C[r][c]=A[r][c]+B[r][c]. The outer loop selects the row and the inner loop the column.
  • Each completed row is appended to C, building a new nested list.
  • The printing loop uses \texttt{end='\textbackslash t'} to keep a row on one line and \texttt{print()} to move to the next row.
  • The main diagonal elements have equal row and column indexes, so 1+5+9=15.
Part of the trace table (every sum is 10)
rcA[r][c] + B[r][c]
001 + 9 = 10
012 + 8 = 10
126 + 4 = 10
229 + 1 = 10
38 marks
Write a Python program that copies the contents of one text file into another and counts the lines, words and characters of the file.
Solution

Program

def copy_and_count(source, target):
    lines = words = chars = 0
    with open(source, "r") as src, open(target, "w") as dst:
        for line in src:                  # a file object gives one line at a time
            dst.write(line)
            lines += 1
            words += len(line.split())
            chars += len(line)
    print(f"Copied {source} to {target}")
    print("Lines:", lines, "| Words:", words, "| Characters:", chars)

copy_and_count("poem.txt", "backup.txt")
with open("backup.txt") as f:
    print(f.read(), end="")
Copy a file and count lines, words and characters
Copied poem.txt to backup.txt
Lines: 2 | Words: 10 | Characters: 54
Twinkle twinkle little star
How I wonder what you are
Output (poem.txt holds two lines of a poem)

Explanation

  • The source is opened in read mode and the target in write mode in one \texttt{with} statement, so both close automatically.
  • Looping over a file object gives one line at a time; each line is written unchanged to the target.
  • \texttt{split()} breaks a line into words at spaces, so \texttt{len(line.split())} is the number of words.
  • \texttt{len(line)} counts characters including the end-of-line character: 28+26=54.
48 marks
Explain the four principles of object oriented programming (encapsulation, abstraction, inheritance and polymorphism) with the help of one Python program.
Solution

The four principles

  • Encapsulation: data and methods are bundled in a class and the data is changed only through methods. Here the balance is changed only by \texttt{deposit()} and \texttt{monthly\_update()}.
  • Abstraction: the user sees only what is needed; \texttt{describe()} gives a simple summary and the calculation details stay hidden inside the class.
  • Inheritance: SavingsAccount and CurrentAccount are derived from Account, so they get the constructor, \texttt{deposit()} and \texttt{describe()} without rewriting them.
  • Polymorphism: both child classes have their own \texttt{monthly\_update()}; the same call adds profit to a savings account but deducts a charge from a current account.

Program

class Account:                                  # parent class
    def __init__(self, holder, balance=0):
        self.holder = holder
        self._balance = balance                 # protected: use the methods below

    def deposit(self, amount):                  # encapsulation: controlled change
        if amount > 0:
            self._balance += amount

    def get_balance(self):
        return self._balance

    def monthly_update(self):                   # common operation
        pass

    def describe(self):                         # abstraction: user sees a simple summary
        return f"{self.holder}: Rs. {self._balance}"

class SavingsAccount(Account):                  # inheritance
    def monthly_update(self):                   # polymorphism: own version
        self._balance += self._balance * 0.01   # 1% profit

class CurrentAccount(Account):
    def monthly_update(self):
        self._balance -= 100                    # service charge

accounts = [SavingsAccount("Hira", 50000), CurrentAccount("Usman", 20000)]
for acc in accounts:
    acc.deposit(1000)
    acc.monthly_update()                        # same call, different behaviour
    print(type(acc).__name__, "->", acc.describe())
All four OOP principles in one program
SavingsAccount -> Hira: Rs. 51510.0
CurrentAccount -> Usman: Rs. 20900
Output

Working

Hira: 50000+1000=51000, plus 1% profit =51510. Usman: 20000+1000=21000, minus 100 =20900. The loop does not check the account type; each object applies its own rule.

58 marks
What is data normalization? Explain 1NF, 2NF and 3NF with the help of an example.
Solution

Definition

Data normalization is the process of organising a database into smaller, related tables, following rules called normal forms, so that redundancy (repeated data) and dependency problems are removed. It prevents update, insert and delete anomalies and keeps data consistent.

Example: unnormalized table

RollNoNameClassClassTeacherSubjects
1Ali12-AMr. KhanPhysics, CS
2Sana12-AMr. KhanMaths, CS

First normal form (1NF)

Every cell must hold one (atomic) value. The Subjects cell holds two values, so each subject gets its own row. The key becomes (RollNo, Subject).

RollNoSubjectNameClassClassTeacher
1PhysicsAli12-AMr. Khan
1CSAli12-AMr. Khan
2MathsSana12-AMr. Khan
2CSSana12-AMr. Khan

Second normal form (2NF)

Every non-key attribute must depend on the whole key. Name and Class depend only on RollNo (part of the key), a partial dependency. They move to a Student table; the linking table keeps only (RollNo, Subject).

Student
RollNoNameClassClassTeacher
1Ali12-AMr. Khan
2Sana12-AMr. Khan
StudentSubject
RollNoSubject
1Physics
1CS
2Maths
2CS

Third normal form (3NF)

No non-key attribute may depend on another non-key attribute. ClassTeacher depends on Class, not on RollNo (RollNo \to Class \to ClassTeacher is a transitive dependency). A Class table is created.

Student
RollNoNameClass
1Ali12-A
2Sana12-A
Class
ClassClassTeacher
12-AMr. Khan

Now if the class teacher changes, only one row is updated instead of every student's row.

68 marks
Write a Python program using the sqlite3 module to create a table Student, insert four records, display the students who scored above 80 in descending order of marks, and delete a record.
Solution

Program

import sqlite3

conn = sqlite3.connect("college.db")
cur = conn.cursor()
cur.execute("DROP TABLE IF EXISTS Student")
cur.execute("CREATE TABLE Student (RollNo INTEGER PRIMARY KEY, Name TEXT NOT NULL, Marks INTEGER)")
cur.executemany("INSERT INTO Student VALUES (?, ?, ?)",
                [(1, "Ali", 72), (2, "Sana", 91), (3, "Bilal", 85), (4, "Maryam", 64)])
conn.commit()

print("Students with marks above 80 (highest first):")
cur.execute("SELECT Name, Marks FROM Student WHERE Marks > 80 ORDER BY Marks DESC")
for name, marks in cur.fetchall():
    print(" ", name, marks)

cur.execute("DELETE FROM Student WHERE RollNo = ?", (4,))
conn.commit()
cur.execute("SELECT COUNT(*) FROM Student")
print("Records left:", cur.fetchone()[0])
conn.close()
Student table in SQLite
Students with marks above 80 (highest first):
  Sana 91
  Bilal 85
Records left: 3
Output

Explanation

  • \texttt{connect()} opens (or creates) college.db and the cursor executes SQL. The old table is dropped so the program can be run again.
  • \texttt{CREATE TABLE} defines RollNo as the primary key and Name as NOT NULL.
  • \texttt{executemany()} inserts four records in one call with ? placeholders; \texttt{commit()} saves them.
  • \texttt{WHERE Marks > 80 ORDER BY Marks DESC} selects Sana (91) and Bilal (85), highest first.
  • \texttt{DELETE ... WHERE RollNo = 4} removes Maryam's record; \texttt{COUNT(*)} confirms 3 records are left.
78 marks
Write a Python program with functions to count the vowels in a string, reverse a string and check whether a string is a palindrome.
Solution

Program

def count_vowels(text):
    return sum(1 for ch in text.lower() if ch in "aeiou")

def reverse(text):
    return text[::-1]                    # slice with step -1 reads backwards

def is_palindrome(text):
    cleaned = "".join(ch.lower() for ch in text if ch.isalnum())
    return cleaned == reverse(cleaned)

for word in ["Level", "Python", "Madam, I'm Adam"]:
    print(f"{word!r}: vowels = {count_vowels(word)}, reversed = {reverse(word)!r}, "
          f"palindrome = {is_palindrome(word)}")
String functions
'Level': vowels = 2, reversed = 'leveL', palindrome = True
'Python': vowels = 1, reversed = 'nohtyP', palindrome = False
"Madam, I'm Adam": vowels = 5, reversed = "madA m'I ,madaM", palindrome = True
Output

Explanation

  • \texttt{count\_vowels()} converts the text to lower case and counts the characters that are in "aeiou".
  • \texttt{reverse()} uses the slice \texttt{[::-1]}: start to end with step -1, which reads the string backwards.
  • \texttt{is\_palindrome()} first keeps only letters and digits in lower case (\texttt{isalnum()}), so spaces, commas and capital letters do not matter, then compares the cleaned string with its reverse.
  • "Madam, I'm Adam" becomes "madamimadam", which reads the same both ways, so it is a palindrome.

FBISE 2026 questions

Questions from the FBISE HSSC-II 2026 (1st Annual) papers that belong to this unit, answered the way an examiner expects.

2026 Annual I, Set B, Q.1 (6)1 mark
Which Object-Oriented Programming (OOP) concept allows creating new classes from existing ones to reduce code repetition?
  1. AEncapsulation
  2. BInheritanceAnswer
  3. CAbstraction
  4. DPolymorphism
Why

Inheritance creates a derived (child) class from an existing parent class; the child reuses the parent's attributes and methods, so code is not repeated.

2026 Annual I, Set B, Q.2 (vi)1+1+1 = 3 marks
What is meant by data normalization? State any two forms of normalization.
Solution

Data normalization (1 mark)

Data normalization is a database design technique that divides data into smaller, related tables linked by keys, so that redundancy (duplicate data) is reduced and inconsistencies during insertion, update and deletion are avoided.

Two forms (1 + 1 marks)

  • First normal form (1NF): every cell holds a single (atomic) value; repeating groups and multi-valued cells are removed.
  • Second normal form (2NF): the table is in 1NF and every non-key attribute depends on the whole primary key (no partial dependency).
  • (Also: Third normal form (3NF): the table is in 2NF and no non-key attribute depends on another non-key attribute, that is, no transitive dependency.)
2026 Annual I, Set B, Q.2 (ix) OR1.5+1.5 = 3 marks
How is a file opened and closed in Python?
Solution

Opening a file (1.5 marks)

A file is opened with the built-in \texttt{open()} function, which takes the file name (with path if needed) and the mode ("r" read, "w" write, "a" append, "rb"/"wb" binary). It returns a file object (file handler) used for all later operations: \texttt{f = open("marks.txt", "w")}.

Closing a file (1.5 marks)

The file is closed with \texttt{f.close()}, which saves all data to the disk and frees the memory used by the file. Alternatively the \texttt{with} statement opens the file and closes it automatically when the block ends, even if an error occurs.

f = open("marks.txt", "w")      # open: returns a file object
f.write("Ali 85\n")
f.close()                       # close: saves data, frees memory

with open("marks.txt", "r") as f:   # opened here ...
    print(f.read(), end="")
# ... and closed automatically here
Opening and closing both ways
Ali 85
Output
2026 Annual I, Set B, Q.61+2+2 = 5 marks
What is Functional Programming? Also write down two pros and two cons of Functional Programming.
Solution

Functional programming (1 mark)

Functional programming is a programming paradigm in which the program is divided into functions according to their functionality. The solution is broken into segments and each segment is handled by a function. The programmer focuses on what is to be computed rather than how, and functions return results without changing data outside themselves (no change of state, no side effects). Python supports this style with functions, \texttt{map()}, \texttt{filter()} and lambda.

def square(n):
    return n * n

print(list(map(square, [1, 2, 3, 4])))
[1, 4, 9, 16]
Output

Two pros (2 marks)

  • Simpler, concise code that is easy to debug: each function does one job, so its logic is easy to follow and an error is traced to one function; a change affects only that function.
  • Easy to test and suited to parallel work: a function's result depends only on its inputs, so it is tested on its own; because state is not shared and changed, functions can run in parallel (concurrently).

Two cons (2 marks)

  • Not easy for beginners: thinking in functions without changing variables needs practice, and complex algorithms may need higher-order functions that are hard to express.
  • Overhead and lower performance: creating new data instead of changing existing data costs extra memory and time, especially with large data structures, so it may be less efficient in performance-critical programs.
2026 Annual I, Set D, Q.1 (5)1 mark
Which of the following are key concepts of Object-Oriented Programming (OOP)?
  1. ASegmentation of code into functions
  2. BEncapsulation, Abstraction, InheritanceAnswer
  3. CEmphasis on program flow only
  4. DWriting code without using objects
Why

Encapsulation, abstraction and inheritance (with polymorphism) are the pillars of OOP. Dividing code into functions is the functional/procedural idea, and OOP is built on objects.

2026 Annual I, Set D, Q.2 (vi)2+1 = 3 marks
Write down the concept of primary key. Give an example.
Solution

Concept (2 marks)

A primary key is a field, or a minimal set of fields, whose value uniquely identifies each record in a table. Its value can never be repeated and can never be empty (NULL), and each table has only one primary key. It is chosen from the candidate keys, and other tables link to the record through it as a foreign key. It prevents duplicate records and makes searching fast.

Example (1 mark)

In the Customer table, \texttt{C\_ID} is the primary key: C1, C2 and C3 each identify exactly one customer, although two customers may have the same name.

C_ID (primary key)C_NameC_Phone
C1Qamar123-4567
C2Mahtab987-6543
C3Chaand543-2109
CREATE TABLE Customer (C_ID TEXT PRIMARY KEY, C_Name TEXT, C_Phone TEXT);
2026 Annual I, Set D, Q.2 (ix) OR1x3 = 3 marks
Write down three drawbacks of functional programming.
Solution
  1. Not easy for beginners: it needs practice to solve problems only with functions and without changing variables.
  2. Overhead with large data: because data is not changed in place, new copies are created, which uses extra memory and time when handling large data structures; it may be less efficient in performance-critical tasks.
  3. Complex algorithms are harder to express: they often need higher-order functions (functions that take or return functions), and sharing data between functions needs careful handling and synchronisation.
2026 Annual I, Set D, Q.2 (xi) OR1x3 = 3 marks
What is the difference between text and binary files? Any three points.
Solution
Text fileBinary file
Stores data as readable characters (ASCII/Unicode).Stores data as raw bytes (0s and 1s), the same form as in memory.
Each line ends with an end-of-line marker (\texttt{\textbackslash n}); characters may be translated when read or written.No end-of-line marker and no translation; bytes are stored exactly.
Can be read and edited by a person in any text editor.Cannot be read by a person; needs the program that created it.
Usually larger for numbers (12345 needs 5 characters).Usually smaller and faster to read and write (an int takes 4 bytes).
Examples: .txt, .csv, .pyExamples: .jpg, .png, .mp3, .mp4, .exe

Any three rows earn the three marks. In Python a text file is opened with "r"/"w" and a binary file with "rb"/"wb".

2026 Annual I, Set D, Q.61+2+2 = 5 marks
What is Object-Oriented Programming (OOP)? Also write down two pros and two cons of OOP.
Solution

Object-oriented programming (1 mark)

OOP is a programming paradigm based on objects. A class is a template that defines attributes (data) and methods (functions); an object is an instance of a class that models a real-world thing such as a car or a bank account. The emphasis in problem solving is on objects rather than logic, and OOP uses encapsulation, abstraction, inheritance and polymorphism.

class Car:
    def __init__(self, color):
        self.color = color
    def start(self):
        return self.color + " car started"

c = Car("Red")
print(c.start())
Red car started
Output

Two pros (2 marks)

  • Reusability and simpler complex systems: a big system is broken into smaller reusable classes, and inheritance lets new classes reuse existing code.
  • Security and maintainability: encapsulation hides implementation details and protects data from direct access, so code is safer and easier to maintain; objects also represent real-world things naturally.

Two cons (2 marks)

  • Complex design: if not planned carefully, class hierarchies become complex, and inheritance can make the design rigid and less flexible.
  • Harder to understand and not always efficient: encapsulation and polymorphism can make code harder to follow and debug, and OOP may not suit computationally intensive tasks.
2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (3), old syllabus1 mark
What will be displayed after executing the following code?
int a = 5, b = 10, c;
c = a++ * --b;
cout << c;
  1. A45Answer
  2. B50
  3. C54
  4. D60
Why

\texttt{a++} is post-increment: the old value 5 is used, then a becomes 6. \texttt{--b} is pre-decrement: b becomes 9 before it is used. So c=5\times9=45.

#include <iostream>
using namespace std;
int main() {
    int a = 5, b = 10, c;
    c = a++ * --b;
    cout << c << endl;
    cout << "a = " << a << ", b = " << b << endl;
    return 0;
}
45
a = 6, b = 9
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (5), old syllabus1 mark
If a = 12; b = a++; what will be the value stored in b?
  1. A13
  2. B0
  3. C12Answer
  4. D11
Why

Post-increment assigns the old value first (b = 12) and then increases a to 13.

#include <iostream>
using namespace std;
int main() {
    int a = 12, b;
    b = a++;
    cout << "b = " << b << ", a = " << a << endl;
    cout << "sizeof(float) = " << sizeof(float) << endl;
    return 0;
}
b = 12, a = 13
sizeof(float) = 4
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (6), old syllabus1 mark
In C++, how many bytes are reserved for a variable of datatype 'float'?
  1. A2
  2. B4Answer
  3. C6
  4. D8
Why

A float is a single-precision real number stored in 4 bytes (32 bits); a double takes 8 bytes. The program in the previous question prints sizeof(float) = 4.

2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (7), old syllabus1 mark
If x = 5, which one of the following accesses the seventh element stored in an array A?
  1. AA[x++]
  2. BA[x]
  3. CA[7]
  4. DA[++x]Answer
Why

Array indexes start at 0, so the seventh element is A[6]. \texttt{++x} increases x to 6 before it is used, giving A[6]. \texttt{A[x++]} and \texttt{A[x]} use A[5] (sixth element) and A[7] is the eighth element.

#include <iostream>
using namespace std;
int main() {
    int A[10] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
    int x = 5;
    cout << A[++x] << endl;      // x becomes 6 first: A[6] is the 7th element
    return 0;
}
70
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (8), old syllabus1 mark
How many parameters can be passed to a function at a time?
  1. AOne parameter
  2. BTwo parameters
  3. CThree parameters
  4. DAny number of parametersAnswer
Why

A function may be defined with no parameters or with as many parameters as it needs; there is no fixed limit of one, two or three.

2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (9), old syllabus1 mark
The dereference operator is denoted by:
  1. A*Answer
  2. B&
  3. C**
  4. D&&
Why

\texttt{*p} gives the value stored at the address held in pointer p. \texttt{\&} is the address-of (reference) operator and \texttt{\&\&} is logical AND.

2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (10), old syllabus1 mark
Which one of the following is an arithmetic operator?
  1. A&&
  2. B<=
  3. C%Answer
  4. D++
Why

% (modulus) gives the remainder of a division and is one of the arithmetic operators + - * / %. && is logical, <= is relational and ++ is the increment (unary) operator.

2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (11), old syllabus1 mark
Which one of the following is the default access specifier of C++ class?
  1. AProtected
  2. BPublic
  3. CPrivateAnswer
  4. DDefault
Why

Members of a C++ class are private unless a specifier is written, which supports encapsulation. (In a struct the default is public.)

2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (12), old syllabus1 mark
In C++, Ofstream is classified as a(n):
  1. ACommand
  2. BClassAnswer
  3. CObject
  4. DMethod
Why

ofstream is a class of the fstream library for writing to files; we create an object of it, for example \texttt{ofstream fout("data.txt");}.

2026 Annual I, Old syllabus (Curriculum 2009), Q.1 (13), old syllabus1 mark
When writing one character at a time to a file the following function is used:
  1. Aget()
  2. Bfputs()
  3. Cfgets()
  4. Dput()Answer
Why

\texttt{put()} writes a single character to an output stream (file). get() reads one character; fputs() and fgets() handle whole strings in C.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (i) OR, old syllabus1x3 = 3 marks
Write down the purpose of the following escape sequences: I. \texttt{\textbackslash a} II. \texttt{\textbackslash b} III. \texttt{\textbackslash n}
Solution
  1. \texttt{\textbackslash a} (alert): produces a beep (bell) sound from the computer.
  2. \texttt{\textbackslash b} (backspace): moves the cursor one position back, so the next character overwrites the previous one.
  3. \texttt{\textbackslash n} (new line): moves the cursor to the beginning of the next line.
#include <iostream>
using namespace std;
int main() {
    cout << "Line 1\nLine 2" << endl;     // \n: new line
    cout << "ABC\bD" << endl;             // \b: backspace, D overwrites C on screen
    cout << "\a" << "Beep!" << endl;      // \a: alert (bell sound)
    return 0;
}
Line 1
Line 2
ABC<BS>D
Beep!
Output (the bell is not visible; <BS> marks the backspace character sent to the screen, where D appears over C)
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (ii), old syllabus1+1+1 = 3 marks
Write down the reasons of the following invalid variable names: I. 2a II. S$ III. Int
Solution
  1. 2a: a variable name cannot start with a digit; it must start with a letter or an underscore.
  2. S$: special characters such as $ are not allowed; only letters, digits and the underscore may be used.
  3. Int: it looks like the reserved keyword int (data type), and keywords cannot be used as variable names.
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (ii) OR, old syllabus1+2 = 3 marks
Write down the purpose and syntax of break statement.
Solution

Purpose (1 mark)

The break statement immediately ends the loop (for, while, do-while) or switch statement in which it appears; control passes to the first statement after that loop or switch. It is used to leave a loop early when a condition is met, such as when a searched item is found.

Syntax and example (2 marks)

break;
#include <iostream>
using namespace std;
int main() {
    for (int i = 1; i <= 10; i++) {
        if (i == 5)
            break;              // leave the loop when i is 5
        cout << i << " ";
    }
    cout << endl << "Loop ended";
    return 0;
}
1 2 3 4 
Loop ended
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (iii), old syllabus1x3 = 3 marks
Write down any three differences between text and binary files.
Solution
Text fileBinary file
Stores data as readable characters (ASCII/Unicode).Stores data as raw bytes (0s and 1s), the same form as in memory.
Each line ends with an end-of-line marker (\texttt{\textbackslash n}); characters may be translated when read or written.No end-of-line marker and no translation; bytes are stored exactly.
Can be read and edited by a person in any text editor.Cannot be read by a person; needs the program that created it.
Usually larger for numbers (12345 needs 5 characters).Usually smaller and faster to read and write (an int takes 4 bytes).
Examples: .txt, .csv, .pyExamples: .jpg, .png, .mp3, .mp4, .exe

Any three rows give the three marks.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (iii) OR, old syllabus1x3 = 3 marks
How is Constructor different from Destructor? List down any three differences.
Solution
ConstructorDestructor
Called automatically when an object is created.Called automatically when an object is destroyed (goes out of scope).
Same name as the class: \texttt{Demo()}.Same name with a tilde: \texttt{\textasciitilde Demo()}.
Initialises data members; can take parameters and can be overloaded.Releases resources; takes no parameters and cannot be overloaded (one per class).
#include <iostream>
using namespace std;
class Demo {
public:
    Demo()  { cout << "Constructor called" << endl; }
    ~Demo() { cout << "Destructor called" << endl; }
};
int main() {
    {
        Demo d;                 // object created: constructor runs
        cout << "Inside block" << endl;
    }                           // object goes out of scope: destructor runs
    cout << "After block" << endl;
    return 0;
}
Constructor called
Inside block
Destructor called
After block
Output

In Python the constructor is \texttt{\_\_init\_\_()} and the destructor is \texttt{\_\_del\_\_()}.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (iv) OR, old syllabus3 marks
If p is the pointer variable, what will be the difference between the following statements?
cout<<p;
cout<<*p;
Solution
  • \texttt{cout<<p;} prints the value of the pointer itself, which is a memory address (for example 0x7ffd5c2a), the address of the variable it points to.
  • \texttt{cout<<*p;} uses the dereference operator * and prints the value stored at that address, that is, the value of the variable p points to.
#include <iostream>
using namespace std;
int main() {
    int n = 25;
    int *p = &n;          // p stores the address of n
    cout << (p == &n) << endl;   // cout << p would print this address, e.g. 0x7ffd...
    cout << *p << endl;          // value stored at that address
    return 0;
}
1
25
Output

Here the first line confirms that p holds the address of n (1 means true; printing p itself shows a different address on every run), and \texttt{*p} gives 25, the value of n.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (v), old syllabus3 marks
Write a program to initialize two-dimensional arrays of two different data types.
Solution

A two-dimensional array stores values in rows and columns and is initialised with nested braces, one inner pair per row. Below, an int array holds marks of 2 students in 3 subjects and a float array holds temperatures.

#include <iostream>
using namespace std;
int main() {
    int marks[2][3] = {{85, 90, 78}, {70, 88, 92}};          // integer 2D array
    float temp[2][2] = {{30.5, 31.2}, {28.4, 29.9}};         // float 2D array

    cout << "Marks:" << endl;
    for (int r = 0; r < 2; r++) {
        for (int c = 0; c < 3; c++)
            cout << marks[r][c] << " ";
        cout << endl;
    }
    cout << "Temperatures:" << endl;
    for (int r = 0; r < 2; r++) {
        for (int c = 0; c < 2; c++)
            cout << temp[r][c] << " ";
        cout << endl;
    }
    return 0;
}
Marks:
85 90 78 
70 88 92 
Temperatures:
30.5 31.2 
28.4 29.9 
Output

The nested for loops visit each row (outer loop) and each column (inner loop), just like a nested list in Python.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (v) OR, old syllabus1x3 = 3 marks
Write down the output of the following statements: I. A = (x > 0) && (y < 10) where x = 5, y = 15 II. S = 13 + 21 % 4 III. n *= 2; where n = 10
Solution
  1. I. x>0 is true (1) but y<10 is false (0); true && false is false, so A = 0.
  2. II. % has higher precedence than +, so 21\,\%\,4=1 and S = 13 + 1 = 14.
  3. III. \texttt{n *= 2} means n=n\times2=10\times2, so n = 20.
#include <iostream>
using namespace std;
int main() {
    int x = 5, y = 15, n = 10;
    int A = (x > 0) && (y < 10);
    int S = 13 + 21 % 4;
    n *= 2;
    cout << "A = " << A << endl << "S = " << S << endl << "n = " << n << endl;
    return 0;
}
A = 0
S = 14
n = 20
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (vi), old syllabus1+2 = 3 marks
Write down the purpose and syntax of if else statement.
Solution

Purpose (1 mark)

The if-else statement selects one of two blocks of code. If the condition is true, the if block runs; otherwise the else block runs. It is used for two-way decisions such as pass/fail or even/odd.

Syntax with example (2 marks)

if (condition)
{
    // statements when condition is true
}
else
{
    // statements when condition is false
}
#include <iostream>
using namespace std;
int main() {
    int marks = 72;
    if (marks >= 40)
        cout << "Pass";
    else
        cout << "Fail";
    return 0;
}
Pass
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (vi) OR, old syllabus3 marks
What is the relationship between a class and an object? Discuss briefly.
Solution

A class is a template or blueprint that defines the attributes (data members) and methods (member functions) of a type of thing. An object is an instance of a class: a real entity created from that template, with its own values of the attributes. The class is only a design and uses no memory for data; memory is allocated when objects are created. One class can create many objects, all with the same structure and functions but different data, just as many houses can be built from one map.

#include <iostream>
using namespace std;
class Box {                         // class: the design
public:
    int length;
};
int main() {
    Box b1, b2;                     // objects: real boxes made from the design
    b1.length = 10;
    b2.length = 25;
    cout << b1.length << " " << b2.length << endl;
    return 0;
}
10 25
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (vii), old syllabus1+2 = 3 marks
Why is it important to write comments in a program? Also differentiate its two types.
Solution

Importance

Comments are notes in the source code that the compiler ignores. They explain what the code does and why, so the program is easier to read, understand, debug and modify later, by the same programmer or by others. They can also switch off a line temporarily during debugging.

Two types

Single-line commentMulti-line comment
Starts with // and ends at the end of that line.Starts with /* and ends with */; may cover several lines.
Used for short notes beside a statement.Used for longer descriptions, such as the purpose of a program or function.
\texttt{x = x + 1; // increase x}\texttt{/* This program finds the average of marks */}

In Python a single-line comment starts with #.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (vii) OR, old syllabus2+1 = 3 marks
Discuss the concept of inheritance with one daily life example.
Solution

Concept (2 marks)

Inheritance is an OOP feature in which a new class (derived or child class) is created from an existing class (base or parent class). The derived class automatically gets the data members and functions of the base class and can add its own. It gives code reusability, reduces repetition and builds a hierarchy of classes (an "is-a" relationship).

Daily life example (1 mark)

A car is a vehicle. The Vehicle class has wheels and start(); the Car class inherits them and adds its own member, seats. Similarly, a child inherits features from its parents.

#include <iostream>
using namespace std;
class Vehicle {                     // base class
public:
    int wheels;
    void start() { cout << "Vehicle started" << endl; }
};
class Car : public Vehicle {        // derived class
public:
    int seats;
};
int main() {
    Car c;
    c.wheels = 4;                   // inherited member
    c.seats = 5;
    c.start();                      // inherited function
    cout << c.wheels << " wheels, " << c.seats << " seats" << endl;
    return 0;
}
Vehicle started
4 wheels, 5 seats
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (viii), old syllabus3 marks
Write down the output of the following program segment:
char c = 'A';
do
{
    cout << c << "\t";
    c = c + 2;
}
while (c <= 'K');
Solution

c starts at 'A'. Each pass prints c followed by a tab and adds 2 to its ASCII code, so the letters are A, C, E, G, I, K. After K, c becomes 'M', which is greater than 'K', so the loop stops.

#include <iostream>
using namespace std;
int main() {
    char c = 'A';
    do
    {
        cout << c << "\t";
        c = c + 2;
    } while (c <= 'K');
    return 0;
}
A	C	E	G	I	K	
Output
Trace table
Passc printedc after c + 2c <= 'K'?
1A (65)C (67)yes
2CEyes
3EGyes
4GIyes
5IK (75)yes
6KM (77)no: stop
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (viii) OR, old syllabus3 marks
Write down the output of the following program segment:
int x = 5;
do {
    cout << x << " ";
    x++;
} while (x < 8);
Solution

The do-while loop runs its body first and tests the condition afterwards. x = 5 is printed and becomes 6; 6 < 8, so 6 is printed and x becomes 7; 7 < 8, so 7 is printed and x becomes 8; now 8 < 8 is false and the loop stops. Output: 5 6 7

#include <iostream>
using namespace std;
int main() {
    int x = 5;
    do {
        cout << x << " ";
        x++;
    } while (x < 8);
    return 0;
}
5 6 7 
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (ix), old syllabus3 marks
Rewrite the following statement using if-else statement:
cout<<(((num%2)==0)?"Even\n":"Odd\n");
Solution

The conditional (ternary) operator \texttt{condition ? a : b} gives a if the condition is true and b otherwise. The same decision with if-else:

if ((num % 2) == 0)
    cout << "Even\n";
else
    cout << "Odd\n";

Tested with num = 7:

#include <iostream>
using namespace std;
int main() {
    int num = 7;
    if ((num % 2) == 0)
        cout << "Even\n";
    else
        cout << "Odd\n";
    return 0;
}
Odd
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (ix) OR, old syllabus2+1 = 3 marks
What is the difference between constant and variable? Give one example.
Solution
ConstantVariable
A named value that cannot change while the program runs.A named memory location whose value can change during execution.
Declared with const and must be given a value at declaration.May be given a value at declaration or later, and changed any number of times.
\texttt{const float PI = 3.14159;}\texttt{int radius = 2; radius = 3;}
#include <iostream>
using namespace std;
int main() {
    const float PI = 3.14159;    // constant: cannot be changed
    int radius = 2;              // variable: can be changed
    radius = 3;
    cout << PI * radius * radius << endl;
    return 0;
}
28.2743
Output

Trying \texttt{PI = 3;} gives a compile-time error.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (x), old syllabus2+1 = 3 marks
What is the purpose of sizeof() function? Illustrate with example.
Solution

Purpose (2 marks)

\texttt{sizeof()} is an operator (used like a function) that returns the number of bytes a data type, variable or array occupies in memory. It helps write portable programs, because sizes may differ between computers, and it is used to find the number of elements of an array: \texttt{sizeof(arr) / sizeof(arr[0])}.

Example (1 mark)

#include <iostream>
using namespace std;
int main() {
    int marks[10];
    cout << sizeof(int) << " " << sizeof(char) << " " << sizeof(double) << endl;
    cout << sizeof(marks) << endl;       // 10 elements x 4 bytes
    return 0;
}
4 1 8
40
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (x) OR, old syllabus2+1 = 3 marks
What is reference operator? Give an example.
Solution

The reference (address-of) operator & gives the memory address of a variable, as in \texttt{int *p = \&n;}. The same symbol is used in a declaration to create a reference variable, another name (alias) for an existing variable; a change through the reference changes the original. References are used to pass arguments by reference so a function can change them.

#include <iostream>
using namespace std;
void addBonus(int &m) {       // m is a reference (another name) for the argument
    m = m + 5;
}
int main() {
    int marks = 70;
    int &r = marks;           // r refers to marks
    r = 80;                   // changes marks
    addBonus(marks);
    cout << marks << endl;
    return 0;
}
85
Output

r = 80 changes marks to 80, and addBonus() adds 5 through its reference parameter, giving 85.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (xi), old syllabus1x3 = 3 marks
Compare local and static variables in terms of scope, lifetime, and storage duration.
Solution
PointLocal variableStatic (local) variable
ScopeOnly inside the function or block where it is declared.Also only inside its function or block.
LifetimeCreated when the function is called and destroyed when it returns; the value is lost.Created once and exists until the program ends; keeps its value between calls.
Storage duration / initialisationAutomatic storage (stack); has garbage value unless initialised; initialised on every call.Static storage; initialised only once (to 0 by default).
#include <iostream>
using namespace std;
void counter() {
    int local = 0;           // created again on every call
    static int total = 0;    // created once, keeps its value between calls
    local++;
    total++;
    cout << "local = " << local << ", static = " << total << endl;
}
int main() {
    counter();
    counter();
    counter();
    return 0;
}
local = 1, static = 1
local = 1, static = 2
local = 1, static = 3
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (xi) OR, old syllabus1x3 = 3 marks
Write down any three differences between actual and formal parameters.
Solution
Actual parameters (arguments)Formal parameters
Written in the function call.Written in the function header (definition).
Can be constants, variables or expressions: sum(x, y), sum(4, 6).Must be variables with data types: int a, int b.
Belong to the calling function.Local to the called function; created when it is called and receive copies (or references) of the actual values.
#include <iostream>
using namespace std;
int sum(int a, int b) {          // a and b: formal parameters
    return a + b;
}
int main() {
    int x = 4, y = 6;
    cout << sum(x, y) << endl;   // x and y: actual parameters (arguments)
    return 0;
}
10
Output
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (xii), old syllabus3 marks
Rewrite the program segment after removing errors:
int a (10), i;
cout>>"enter ten numbers";
for(i=0; i<10; i--)
cin << a(i);
Solution
  • \texttt{int a (10)} declares one integer initialised to 10, not an array: use square brackets \texttt{int a[10]}.
  • cout uses the insertion operator <<, not >>.
  • \texttt{i--} makes i go 0, -1, -2, ... so the loop never ends: use \texttt{i++}.
  • cin uses the extraction operator >>, and array elements are written with square brackets: \texttt{cin >> a[i];}.
int a[10], i;
cout << "enter ten numbers";
for (i = 0; i < 10; i++)
    cin >> a[i];
Corrected segment
#include <iostream>
using namespace std;
int main() {
    int a[10], i;
    cout << "enter ten numbers";
    for (i = 0; i < 10; i++)
        cin >> a[i];
    cout << endl << "First: " << a[0] << ", last: " << a[9] << endl;
    return 0;
}
Tested as a complete program
enter ten numbers
First: 1, last: 10
Output with input 1 2 3 4 5 6 7 8 9 10
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (xiii) OR, old syllabus2+1 = 3 marks
Differentiate between string and array with one example of each.
Solution
ArrayString
A collection of elements of the same data type (int, float, char and so on) in consecutive memory.A sequence of characters used to store text.
Has no end marker; its size is fixed when declared.A C-string is a char array that ends with the null character \texttt{'\textbackslash 0'}; the string class manages its own length.
Processed element by element with a loop.Has ready-made operations: length(), joining with +, comparison.
\texttt{int marks[3] = \{80, 75, 90\};}\texttt{string name = "Ayesha";}
#include <iostream>
#include <string>
using namespace std;
int main() {
    int marks[3] = {80, 75, 90};     // array: many values of one type
    string name = "Ayesha";          // string: sequence of characters
    char city[] = "Lahore";          // C-string: char array ending in '\0'
    cout << marks[1] << " " << name.length() << " " << sizeof(city) << endl;
    return 0;
}
75 6 7
Output

sizeof(city) is 7: six letters of Lahore plus the null character.

2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (xiv), old syllabus3 marks
Define a class Student that contains public data members including function get().
Solution

The class has public data members rollNo, name and marks, and the public member function get() that inputs them; show() displays them. Because the members are public, they can also be used directly from main().

#include <iostream>
#include <string>
using namespace std;

class Student {
public:                         // public data members
    int rollNo;
    string name;
    float marks;

    void get() {                // member function to input the data
        cin >> rollNo >> name >> marks;
    }
    void show() {
        cout << rollNo << " " << name << " " << marks << endl;
    }
};

int main() {
    Student s;                  // object of class Student
    s.get();
    s.show();
    return 0;
}
12 Hina 88.5
Output with input: 12 Hina 88.5
2026 Annual I, Old syllabus (Curriculum 2009), Q.2 (xiv) OR, old syllabus1x3 = 3 marks
Write down any three advantages of using function overloading.
Solution

Function overloading means defining several functions with the same name but different parameter lists (number or types). It is a form of polymorphism.

  1. Readability: one meaningful name (area) is used for similar tasks instead of many names (areaSquare, areaRectangle).
  2. Flexibility: the same function can be called with different types or numbers of arguments; the compiler picks the right version automatically.
  3. Less effort and easier maintenance: the programmer remembers fewer names and related code stays together, which makes the program easier to extend.
#include <iostream>
using namespace std;
int area(int side) { return side * side; }                   // square
int area(int l, int w) { return l * w; }                     // rectangle
double area(double r) { return 3.14 * r * r; }               // circle
int main() {
    cout << area(4) << " " << area(5, 3) << " " << area(2.0) << endl;
    return 0;
}
16 15 12.56
Output

Exam focus

  1. What is functional programming? Two pros and two cons (and its drawbacks). (long)
    Asked in 2026 Set B Section C (5 marks) and Set D Q.2 OR; learn Table 3.1.
    Go to answer
  2. What is OOP? Two pros and two cons; four pillars with examples. (long)
    2026 Set D Section C (5 marks) and MCQs on inheritance and OOP concepts in both sets.
    Go to answer
  3. Data normalization and 1NF, 2NF, 3NF with an example. (short)
    2026 Set B Q.2 (vi); book SRQ 9; a natural long question.
    Go to answer
  4. Primary, candidate, foreign and composite keys. (short)
    2026 Set D Q.2 (vi) and book exercise MCQs 8 and 10.
    Go to answer
  5. Text versus binary files; opening and closing a file; file modes. (short)
    2026 Set D Q.2 (xi) OR, Set B Q.2 (ix) OR, old syllabus Q.2 (iii); book SRQs 5 and 6.
    Go to answer
  6. File programs: write then read data.txt, append a line, count letters, copy a file. (long)
    Book ERQs 5, 6, 9 and 13; the most likely write-a-program questions.
    Go to answer
  7. List, tuple, set and dictionary operations and outputs (insert, append, remove, pop, del, KeyError). (short)
    Book exercise MCQs 16 and 17 and SRQs 11 and 12 test predicting output.
    Go to answer
  8. Nested lists: matrix printing, transpose, symmetric matrix. (long)
    Book ERQ 8 and Activities 3 and 4; trace tables earn marks.
    Go to answer
  9. Python sqlite3 program: insert, update, retrieve; SQL commands. (long)
    Book ERQ 10 and the whole of Section 3.2.10.
    Go to answer
  10. Unit testing with assert and unittest; breakpoints and watchpoints. (long)
    Book ERQ 11, SRQ 10 and the Fig. 3.34 question on which test fails.
    Go to answer
  11. Class programs such as Rectangle with area and perimeter. (long)
    Book ERQ 2; old-syllabus paper also asked for a Student class.
    Go to answer

Mark this unit as done to fill the Class 12 Computer Science ring.

Next, unit 4
Data and analysis

© 2026 FBISE Fold. Independent study notes, not affiliated with the Federal Board of Intermediate and Secondary Education.