๐Ÿ PythonManual
๐Ÿ”
Open Python Code Lab

๐Ÿ“ข The print() Function

The print() function is used to output text, numbers, or variables to the screen.

print("hello", "\nworld")

Spacing: You can use a comma , to combine variables, strings, and numbers in the same print statement. Python automatically adds a space between items separated by a comma.

Concatenation: You can also use the + operator to combine items, but be careful: you can only concatenate strings with other strings, or add numbers to other numbers. Mixing them will cause a type error! The benefit of + is that it doesn't add any spaces automatically.

๐Ÿ“ฆ Variable Assignment

Variables are like boxes that store values. You assign a value to a variable using a single equals sign =.

# Assigning a number
a = 5

# Assigning text (strings)
a = "hello"

โž— Python Operators

Python uses standard symbols to do calculations. Below is your quick reference guide. Click on any example value to evaluate it live in the simulator!

Symbol
What it does
Example
Result
+
Addition
print(8 + 2)
10
-
Subtraction
print(8 - 2)
6
*
Multiplication
print(8 * 2)
16
/
Division
print(10 / 4)
2.5
%
Modulus (Remainder)
print(10 % 3)
1
//
Floor Divide (Whole number)
print(10 // 4)
2
**
Exponent (Power of)
print(10 ** 4)
10000
* (on text)
String Multiplication
print("7" * 2)
77

๐Ÿ’ป Calculations in Python

This program demonstrates how you can combine different operators to perform calculations.

# Assigning values to variables
width = 10
height = 3

# Basic arithmetic
perimeter = (width + height) * 2
area = width * height

# Division, floor division, and modulus
items = 10
people = 3
share = items / people       # 3.333... (float division)
even_share = items // people # 3 (integer division)
leftover = items % people    # 1 (remainder)

print("Area:", area)
print("Share:", share)
print("Remainder:", leftover)

๐Ÿ“ฅ Read User Inputs

The input() function allows programs to pause and ask the user for information.

# 1. Text Inputs (Default)
name = input("Hi! What's your name? ")

By default, everything entered is treated as Text (a string). You cannot perform mathematical operations on text, even if the user types in numbers like 123.

# 2. Number Inputs
MyAge = int(input("When were you born? "))

By wrapping input() inside int(), the text input is converted into an **Integer (a whole number)**, allowing you to calculate values with it.

โฐ Time Delays (time.sleep)

You can create pauses or delays in your code using the time module. Please note in run mode the entire program runs at once and so you will only see the completed code. Display mode will not wait for the sleep function.

import time

print("Wait for it...")
time.sleep(2)  # Pause for 2 seconds
print("Worth waiting for!")

๐Ÿงต F-Strings (Formatted String Literals)

F-strings provide a concise, readable way to format strings in Python. They allow you to embed variables, numbers, and expressions directly inside a string by prefixing it with an f and wrapping the variables or expressions in curly braces {}.

name = "Aisha"
print(f"Hello, {name}!")  # Output: Hello, Aisha!

Crucial Rule: Always remember to put the letter f (lowercase or uppercase) right before the opening quote. If you forget the f, Python will treat it as a normal string and print the braces and variable name literally (e.g., "Hello, {name}!").

๐Ÿงช Interactive F-String Builder

Experiment with how variables and values are dynamically formatted inside f-strings. Change the values below to see the generated code and output immediately:

Python Code:
# Code will be generated here
Program Output:
# Output will be displayed here

โœจ Key Features & Course Topics

Here are the key patterns and formatting options for using f-strings, as covered in our F-String Comic Academy course:

1. Multiple Variables

You can place multiple variables inside a single f-string to build complete sentences.

hero = "Nova"
city = "Byte City"
power = "lightning"
print(f"{hero} protects {city} using {power}!")
2. Integers and Mixed Types

Normally, combining strings and numbers with + causes a TypeError. F-strings handle numbers automatically, without needing to call str().

player = "Pixel"
score = 95
# No need for str(score)!
print(f"{player} scored {score} points!")
3. Expressions & Calculations

Braces can contain simple calculations or operations. Python evaluates them before inserting the result into the string.

coins = 12
bonus = 5
print(f"You now have {coins + bonus} coins.")  # Output: You now have 17 coins.
4. String Methods

You can run string methods directly inside the braces. This returns a modified copy for display without changing the original variable.

hero = "nova"
print(f"Hero: {hero.upper()}")  # Output: Hero: NOVA
5. Money Formatting (2 Decimal Places)

Use the format specifier :.2f inside the braces to round and display numbers with exactly two decimal places.

price = 7.5
# :.2f ensures two decimals (ideal for currency)
print(f"Price: ${price:.2f}")  # Output: Price: $7.50
6. Percentage Formatting

Use the format specifier :.1% to multiply a decimal by 100, display one decimal place, and append a percentage sign automatically.

rate = 3 / 4
print(f"Win rate: {rate:.1%}")  # Output: Win rate: 75.0%

๐ŸŽ“ F-String Comic Academy

Put your knowledge to the test! Learn to master f-strings step-by-step through a series of fun comic missions featuring Byte and Pixel.

๐Ÿš€
F-String Comic Academy 10 levels of beginner f-string challenges.
Open Course โ†—

๐Ÿ’ก Common Mistakes & Best Practices

Keep these rules in mind to avoid errors in your code:

  • Missing the 'f': Writing "{name} scored {score}!" prints the variable names literally. Always add the leading f before the opening quote.
  • Quote Nesting: If your f-string uses double quotes, use single quotes inside the braces (and vice-versa) to avoid syntax errors:
    print(f"Greeting: {user['name']}")
  • Keep it Clean: Only use f-strings when you have variables or expressions to insert. For fixed text, plain strings are faster and clearer. Avoid putting complex logic in braces; compute it first and store in a variable.

๐Ÿ”€ Selection: If & Else

Selection allows programs to make decisions and execute different blocks of code depending on conditions.

test = input("Is it raining? y/n ")
if test == "y":
    print("Oh dear, no football today!")
else:
    print("Great, let's go and play football!")

๐Ÿ”€ Elif (Else If)

Use elif when you need to check multiple conditions sequentially. Once a condition is met, the subsequent checks are skipped.

age = 18

if age < 17:
  print("You are not old enough to drive or vote.")
elif age == 17:
  print("You are old enough to drive!")
else:
  print("You are old enough to drive and vote!")

๐Ÿง  Quick Quiz: Selection

Given this code block, what will it print if the user inputs 18?

age = int(input("Age: "))
if age >= 17:
    print("Drive!")
elif age >= 18:
    print("Vote!")
else:
    print("Wait!")

๐Ÿ” Iteration: While Loops

Loops allow computers to repeat instructions quickly. A while loop repeats as long as a condition is true.

love_status = "y"
while love_status == "y":
    love_status = input('Do you love iteration? y/n ')

๐Ÿ›‘ The Break Statement

You can end a loop instantly using the break statement, even if the loop condition is still true.

while True:
  user_input = input("Enter Q to quit: ")
  if user_input == "Q":
    break

๐Ÿ” For Loops & range()

A for loop repeats a set number of times. You can use range() to customize the loop sequence.

# 1. Repeat 20 times (0 to 19)
for i in range(20):
    print(i)

# 2. Start, Stop, and Steps
# Goes from 1 to 99 in steps of 2 (stops before 100)
for i in range(1, 100, 2):
    print(i)

๐Ÿ“ String Length: len()

The len() function returns the number of characters in a string.

greeting = "Good Day!"
print(len(greeting)) # Outputs: 9

๐Ÿ” Logical Operator: or

Use the or operator to check if at least one of multiple statements is true.

number = 0
while number < 15 or number > 20:
    print("Please enter a number between 15 and 20")
    number = int(input("enter a number: "))

๐Ÿ—‚๏ธ Working with Lists

Lists store collections of items under a single variable name using square brackets [ ].

๐Ÿš€
Python List Maker Quickly format and generate lists of random numbers or custom items.
Open Generator โ†—
# Empty List
emptylist = []

# List with items
MyList = ["Kuala Lumpur", "London", "Paris", "New York", "Bangkok"]

Indexing: Lists start counting from index 0.
โ€ข Print index 0: print(MyList[0]) -> Prints Kuala Lumpur.
โ€ข Print a range: print(MyList[0:3]) -> Prints indexes 0, 1, 2.
โ€ข Format range print: print(*MyList[0:3], sep=", ") -> Removes brackets and separates with commas.
โ€ข Right-to-left: print(MyList[-1]) -> Prints the last item Bangkok.

๐Ÿ› ๏ธ Interactive List Playground

Play with the list visualization below! Modify items or slice ranges to see the changes visually.

๐Ÿข Turtle Graphics Module

The turtle module is a built-in Python library used to introduce programming concepts through drawing shapes, lines, and patterns on a cartesian canvas. By controlling an on-screen cursor (the "turtle") using commands like forward() and right(), you can create custom graphics, explore geometry, and practice core concepts like loops, variables, and custom procedures.

# Start your Python script by importing the turtle module:
import turtle
This complete first example draws a simple zigzag path. It shows that turtle drawings are made from small movement commands: move forward, turn, then move again.
import turtle

turtle.speed(3)
turtle.pensize(8)
turtle.pencolor("black")

turtle.penup()
turtle.goto(-160, 0)
turtle.setheading(45)
turtle.pendown()

for step in range(5):
    turtle.forward(45)
    turtle.right(90)
    turtle.forward(45)
    turtle.left(90)

turtle.hideturtle()
print("Zigzag complete")

๐Ÿ“ Turtle Examples to Try

These examples are designed for the main Python editor. Click Edit on any snippet to open it in Turtle mode and adapt the drawing.

Colour Wheel

This example draws the same circle many times while turning a little after each one. It is a good first pattern for practising loops, colour lists and repeated rotation.
import turtle

turtle.speed(0)
turtle.bgcolor("midnightblue")
turtle.pensize(2)

colours = ["cyan", "gold", "hotpink", "lime", "orange", "white"]

for i in range(36):
    turtle.pencolor(colours[i % len(colours)])
    turtle.circle(70)
    turtle.right(10)

turtle.hideturtle()
print("Colour wheel complete")

Constellation Map

This example uses coordinate pairs with goto() to place points around the canvas. It practises absolute movement without copying any of the course drawing targets.
import turtle

turtle.speed(0)
turtle.bgcolor("midnightblue")
turtle.pencolor("white")

points = [(-150, 90), (-80, 130), (-20, 70), (55, 120), (120, 50)]

for x, y in points:
    turtle.penup()
    turtle.goto(x, y)
    turtle.dot(8, "gold")

turtle.penup()
turtle.goto(points[0])
turtle.pendown()

for x, y in points[1:]:
    turtle.goto(x, y)

turtle.hideturtle()
print("Constellation complete")

Function Flower

This example decomposes the drawing into one reusable petal function. The loop rotates after every petal, then the centre circle is drawn last so it sits neatly on top.
import turtle

turtle.speed(0)
turtle.bgcolor("honeydew")

def petal(colour):
    turtle.color("darkgreen", colour)
    turtle.begin_fill()
    for i in range(2):
        turtle.circle(60, 60)
        turtle.left(120)
    turtle.end_fill()

for i in range(12):
    petal("lightcoral")
    turtle.right(30)

turtle.color("goldenrod", "gold")
turtle.penup()
turtle.goto(0, -25)
turtle.setheading(0)
turtle.pendown()
turtle.begin_fill()
turtle.circle(25)
turtle.end_fill()

turtle.hideturtle()
print("Flower complete")

Labelled Points

This example places labelled points around the canvas. It demonstrates lists, coordinates, goto(), dot() and write() without solving a course drawing task.
import turtle

turtle.speed(0)
turtle.bgcolor("white")
turtle.pencolor("navy")

places = [
    ("Start", -140, 80),
    ("Checkpoint", -20, 20),
    ("Goal", 120, -70)
]

for label, x, y in places:
    turtle.penup()
    turtle.goto(x, y)
    turtle.dot(10, "tomato")
    turtle.forward(12)
    turtle.write(label, font=("Arial", 12, "bold"))

turtle.hideturtle()
print("Labels complete")

๐Ÿƒ Turtle Movement Commands

Control the turtle's direction and coordinate position on the grid. By default, the turtle starts at coordinates (0,0) pointing to the right (East).

Forward and Turn Trail

This example practises forward(), left() and right(). It makes a trail, not a course target shape, so students can focus on how turns affect direction.
import turtle

turtle.speed(4)
turtle.pencolor("royalblue")

turtle.forward(80)
turtle.left(60)
turtle.forward(60)
turtle.right(120)
turtle.forward(60)
turtle.left(60)
turtle.forward(80)

turtle.hideturtle()
print("Trail complete")

Compass Directions

This example uses setheading() to point the turtle in exact compass directions. It shows that 0, 90, 180 and 270 are useful headings.
import turtle

turtle.speed(4)
turtle.pencolor("darkgreen")

for heading in [0, 90, 180, 270]:
    turtle.setheading(heading)
    turtle.forward(80)
    turtle.backward(80)

turtle.hideturtle()
print("Compass complete")

Jump Without Drawing

This example uses penup(), goto() and pendown() to move the turtle without leaving unwanted joining lines.
import turtle

turtle.speed(4)
turtle.pencolor("purple")

positions = [(-120, 40), (-40, -30), (50, 60), (130, -20)]

for x, y in positions:
    turtle.penup()
    turtle.goto(x, y)
    turtle.pendown()
    turtle.forward(35)

turtle.hideturtle()
print("Jump path complete")
turtle.forward(distance)

Moves the turtle forward in its current direction by the specified pixel distance.

Short alias: fd(distance)
turtle.backward(distance)

Moves the turtle backward, opposite to its current heading.

Short aliases: bk(distance), back(distance)
turtle.right(angle)

Turns the turtle clockwise by the specified angle in degrees.

Short alias: rt(angle)
turtle.left(angle)

Turns the turtle counter-clockwise by the specified angle in degrees.

Short alias: lt(angle)
turtle.goto(x, y)

Moves the turtle directly to coordinates (x, y) on the canvas.

Short aliases: setpos(x, y), setposition(x, y)
turtle.setheading(angle)

Sets the turtle direction. 0 is east, 90 is north, 180 is west and 270 is south.

Short alias: seth(angle)
turtle.home()

Moves the turtle back to (0, 0) and points it east.

turtle.xcor() / turtle.ycor()

Returns the current x-coordinate or y-coordinate.

turtle.pos()

Returns the turtle's current position as a coordinate pair.

Short alias: position()
turtle.heading()

Returns the turtle's current heading angle.

๐Ÿ“‹ Movement Activity Cards

Task cards for student activities focusing on movement, coordinates, and basic turning:

โœ๏ธ Pen & Fill Commands

Control the drawing state of the turtle's pen, customize outlines, and fill shapes with colors.

turtle.penup()

Lifts the pen off the canvas. Moving the turtle will not draw any lines.

Short aliases: pu(), up()
turtle.pendown()

Lowers the pen onto the canvas, so movement draws lines again.

Short aliases: pd(), down()
turtle.pencolor(color)

Sets the outline colour, such as "navy", "red" or "#22c55e".

turtle.fillcolor(color)

Sets the colour used between begin_fill() and end_fill().

turtle.color(pen, fill)

Sets both the outline colour and fill colour. With one argument, it sets both to the same colour.

turtle.pensize(width)

Sets the thickness of the drawing outline in pixels.

Short alias: width(width)
turtle.begin_fill()

Call this right before drawing the closed shape you want to fill.

turtle.end_fill()

Call this after drawing the shape. Turtle fills it with the current fill colour.

Colour Swatch Chart

This reference example shows how color(), begin_fill() and end_fill() work together. It draws a swatch chart so students can test supported colour names.
import turtle

colors = ["red", "orange", "yellow", "green", "blue", "purple",
          "pink", "brown", "gray", "black", "white"]

square_size = 30
gap = 15
spacing = square_size + gap

turtle.penup()
turtle.speed(0)

start_x = -130
start_y = 120

for index in range(len(colors)):
    row = index // 6
    col = index % 6

    x = start_x + col * spacing
    y = start_y - row * 90

    # Draw colour swatch
    turtle.goto(x, y)
    turtle.color(colors[index])
    turtle.begin_fill()

    for side in range(4):
        turtle.forward(square_size)
        turtle.right(90)

    turtle.end_fill()

    # Write colour name
    turtle.goto(x, y - 50)
    turtle.color("black")
    turtle.write(colors[index], align="left", font=("Arial", 10, "normal"))

turtle.hideturtle()
print("Colour swatches complete")

Filled Progress Bar

This example uses filled rectangles for a simple interface-style drawing. It demonstrates pen colour, fill colour, and drawing a filled shape without matching the course badge or square tasks.
import turtle

turtle.speed(0)

def rectangle(width, height, outline, fill):
    turtle.color(outline, fill)
    turtle.begin_fill()
    for side in range(2):
        turtle.forward(width)
        turtle.right(90)
        turtle.forward(height)
        turtle.right(90)
    turtle.end_fill()

turtle.penup()
turtle.goto(-150, 30)
turtle.pendown()
rectangle(300, 45, "black", "lightgray")

turtle.penup()
turtle.goto(-145, 25)
turtle.pendown()
rectangle(210, 35, "seagreen", "mediumseagreen")

turtle.penup()
turtle.goto(-30, -40)
turtle.color("black")
turtle.write("70% complete", align="center", font=("Arial", 14, "bold"))

turtle.hideturtle()
print("Progress bar complete")

๐Ÿ“‹ Pen & Fill Activity Cards

Task cards for student activities focusing on line thickness, outlines, and color filling:

๐ŸŽจ Turtle Styling

Change how the turtle and its marks look: cursor shape, drawing speed, dots, circles, labels and visibility.

turtle.circle(radius)

Draws a circle. The centre is one radius to the left of the turtle.

turtle.dot(size, color)

Draws a solid circular dot at the turtle's current position.

turtle.shape(name)

Changes the cursor shape. Try "turtle", "arrow", "circle", "square" or "triangle".

turtle.speed(level)

Sets drawing speed. Use 1 for slow, 10 for fast or 0 for instant drawing.

turtle.write(text, align, font)

Writes text on the canvas. For example, use align="center" to centre the label.

turtle.hideturtle()

Hides the cursor, which makes finished drawings cleaner.

Short alias: ht()
turtle.showturtle()

Makes the turtle cursor visible again.

Short alias: st()

Marker Labels

This example uses dot() and write() to place labelled markers. It is useful for showing how styling commands can add readable annotations to a drawing.
import turtle

turtle.speed(0)
turtle.shape("turtle")
turtle.pencolor("navy")

markers = [
    ("A", -120, 80, "tomato"),
    ("B", 0, 20, "gold"),
    ("C", 120, -60, "mediumseagreen")
]

for label, x, y, colour in markers:
    turtle.penup()
    turtle.goto(x, y)
    turtle.dot(18, colour)
    turtle.forward(14)
    turtle.write(label, font=("Arial", 14, "bold"))

turtle.hideturtle()
print("Markers complete")

Circle Labels

This example uses circle(), dot(), write() and hideturtle() to create labelled markers using supported styling commands.
import turtle

turtle.speed(0)
turtle.pencolor("darkslateblue")

items = [("Small", -110, 0, 25), ("Medium", 0, 0, 40), ("Large", 125, 0, 55)]

for label, x, y, radius in items:
    turtle.penup()
    turtle.goto(x, y - radius)
    turtle.pendown()
    turtle.circle(radius)
    turtle.penup()
    turtle.goto(x, y)
    turtle.dot(8, "tomato")
    turtle.goto(x, y - radius - 24)
    turtle.write(label, align="center", font=("Arial", 10, "bold"))

turtle.hideturtle()
print("Circle labels complete")

๐Ÿ“‹ Styling & Circle Art Activity Cards

Task cards for student activities focusing on complex styling, circle repetition, and combining features:

โ–ฆ Canvas Configuration

The browser Turtle canvas in Python Code Lab uses a fixed visible drawing area of about -200 to 200 on both axes. Use movement and coordinates to position drawings inside that space.

turtle.bgcolor(color)

Sets the background colour of the drawing canvas.

turtle.clear()

Clears the drawing but keeps the turtle's current position and direction.

turtle.reset()

Clears the drawing, resets styles and returns the turtle to (0, 0).

Canvas Coordinate Grid

This example draws the fixed -200 to 200 coordinate area used by the browser Turtle canvas. It helps students understand where goto(x, y) positions are on screen.
import turtle

turtle.speed(0)
turtle.penup()

# Draw the outer square from -200 to 200
turtle.goto(-200, 200)
turtle.pendown()
turtle.pensize(3)

for side in range(4):
    turtle.forward(400)
    turtle.right(90)

# Draw grid lines every 50 pixels
turtle.pensize(1)

for x in range(-150, 200, 50):
    turtle.penup()
    turtle.goto(x, 200)
    turtle.pendown()
    turtle.goto(x, -200)

for y in range(-150, 200, 50):
    turtle.penup()
    turtle.goto(-200, y)
    turtle.pendown()
    turtle.goto(200, y)

# Draw the x-axis and y-axis
turtle.pensize(2)

turtle.penup()
turtle.goto(-200, 0)
turtle.pendown()
turtle.goto(200, 0)

turtle.penup()
turtle.goto(0, 200)
turtle.pendown()
turtle.goto(0, -200)

# Label the corners inside the area
turtle.penup()

turtle.goto(-195, 180)
turtle.write("(-200, 200)", align="left", font=("Arial", 9, "normal"))

turtle.goto(195, 180)
turtle.write("(200, 200)", align="right", font=("Arial", 9, "normal"))

turtle.goto(-195, -195)
turtle.write("(-200, -200)", align="left", font=("Arial", 9, "normal"))

turtle.goto(195, -195)
turtle.write("(200, -200)", align="right", font=("Arial", 9, "normal"))

# Label the centre
turtle.goto(5, 5)
turtle.write("(0, 0)", align="left", font=("Arial", 9, "normal"))

turtle.hideturtle()
print("Canvas grid complete")

Centred Canvas Demo

This example shows the safe configuration commands for this tool while keeping the drawing inside the fixed canvas area. It sets a background colour and places a small labelled panel near the centre.
import turtle

turtle.speed(0)
turtle.bgcolor("lavender")

turtle.penup()
turtle.goto(-95, 45)
turtle.pendown()

turtle.color("navy", "white")
turtle.begin_fill()

for side in range(2):
    turtle.forward(190)
    turtle.right(90)
    turtle.forward(90)
    turtle.right(90)

turtle.end_fill()

turtle.penup()
turtle.goto(0, 8)
turtle.color("navy")
turtle.write("Fixed canvas", align="center", font=("Arial", 14, "bold"))

turtle.goto(0, -22)
turtle.write("Keep drawings near the centre", align="center", font=("Arial", 10, "normal"))

# Try uncommenting one of these lines:
# turtle.clear()
# turtle.reset()

turtle.hideturtle()
print("Canvas demo complete")

๐Ÿ“ Shape Builder Example

This compact example is useful for teaching variables and procedures because students can change the number of sides, size and colours.

import turtle

turtle.speed(0)

def polygon(sides, length, outline, fill):
    turtle.color(outline, fill)
    turtle.begin_fill()
    for i in range(sides):
        turtle.forward(length)
        turtle.right(360 / sides)
    turtle.end_fill()

for sides in range(3, 9):
    polygon(sides, 70, "navy", "lightgreen")
    turtle.penup()
    turtle.forward(95)
    turtle.pendown()

turtle.hideturtle()
print("Shape builder complete")

โ–ฆ 2D Lists: Rows and Columns

A 2D list is a list containing other lists. Use grid[row][column] to access one cell. Rows and columns both start at index 0.

seats = [
    ["A", "A", "B"],
    ["A", "B", "A"],
    ["A", "A", "A"]
]

print(seats[0][2])   # B
seats[1][1] = "A"    # update row 1, column 1

๐ŸŽฏ Pick a Cell, Build the Code

Choose a sample grid, then click a cell. The Python code updates to show the exact grid[row][column] access and a short example of using that value effectively.

Click a cell to see its row and column.

๐Ÿ” Traversing a Grid

Nested loops visit every cell. The outer loop usually controls the row; the inner loop controls each column in that row.

total = 0
sales = [
    [10, 12, 9],
    [7, 11, 15]
]

for row in sales:
    for value in row:
        total = total + value

print(total)

๐Ÿ’พ Reading and Writing Files

File handling lets programs save data after the program stops. Use "r" to read, "w" to overwrite, and "a" to append to the end.

The standard, recommended practice in Python is to use the with statement (context manager). This ensures that files are automatically and properly closed when the block is exited, even if an error/exception occurs during execution.

1. Writing to a File (Overwrite Mode)

Opening a file with "w" writes new data to it. WARNING: This completely overwrites the file. Any old contents will be lost!

# Overwrites the file completely
with open("score.txt", "w") as file:
    file.write("120\n")

2. Appending to a File (Append Mode)

Opening a file with "a" preserves the existing file contents and adds new data at the end.

# Appends data to the end of the file
with open("score.txt", "a") as file:
    file.write("150\n")

3. Reading from a File (Read Mode)

Opening a file with "r" allows you to read from it.

# Reads contents from the file
with open("score.txt", "r") as file:
    first_score = int(file.readline())
    second_score = int(file.readline())

print(first_score)   # 120
print(second_score)  # 150

๐Ÿ“„ Looping Through Lines

Many A-level file tasks load text, split records, count words, or filter rows. strip() removes the newline at the end of each line. We use the with statement here as well to guarantee proper closing.

with open("players.tsv", "r") as file:
    for line in file:
        fields = line.strip().split("\t")
        name = fields[0]
        rating = int(fields[1])
        if rating >= 80:
            print(name)

๐Ÿ“– Dictionaries: Key-Value Pairs

A dictionary stores values under named keys. It is useful when a list index is not meaningful enough.

moon_counts = {
    "Mercury": 0,
    "Earth": 1,
    "Mars": 2
}

print(moon_counts["Earth"])
print(moon_counts.get("Venus", "Not stored"))

Safe access: .get() avoids a KeyError if the key is missing.

๐Ÿ”‘ Keys, Values and List Values

Keys must be unique. If you use the same key twice, the later value replaces the earlier one. Values can be strings, numbers, lists, booleans or even another dictionary.

museum = {
    "Dinosaurs": ["Triceratops", "Stegosaurus"],
    "Planets": ["Mars", "Jupiter"],
    "Open rooms": 6
}

print(museum["Dinosaurs"])
print(museum["Dinosaurs"][0])
print(museum["Open rooms"])

๐Ÿ›ก๏ธ Safe Lookup and Membership

Use in when you need to test whether a key exists. Use .get() when you want a safe fallback value.

shelf_codes = {
    "Graphic novels": "A12",
    "Robotics": "B07",
    "Space": "C04"
}

section = input("Library section: ")

if section in shelf_codes:
    print(shelf_codes[section])
else:
    print("Not found")

print(shelf_codes.get("Poetry", "Ask the librarian"))

๐Ÿ” Dictionary Loops and Frequencies

Dictionaries are excellent for counting frequencies because each word can become a key and its count can become the value.

counts = {}
events = ["login", "save", "login", "print", "save", "login"]

for event in events:
    counts[event] = counts.get(event, 0) + 1

for event, count in counts.items():
    print(event, count)

๐Ÿ” Looping Through Keys, Values and Items

A normal dictionary loop gives keys. Use .values() for values and .items() when you need both the key and the value.

bus_times = {
    "North Gate": "07:40",
    "Sports Hall": "07:55",
    "Library": "08:05"
}

for stop in bus_times:
    print(stop)

for time in bus_times.values():
    print(time)

for stop, time in bus_times.items():
    print(stop, "pickup is", time)
View
Use
.keys()
Shows all keys.
.values()
Shows all stored values.
.items()
Shows key-value pairs, useful for two-variable loops.

โœ๏ธ Updating, Adding, Deleting and Popping

Assigning to a key updates it if the key already exists, or adds a new pair if the key is new. del removes a pair. pop() removes a pair and returns its value.

inventory = {
    "torch": 3,
    "battery": 12,
    "map": 1,
    "rope": 2
}

inventory["torch"] = 4       # update existing key
inventory["compass"] = 1     # add new key

del inventory["rope"]
removed = inventory.pop("battery")

print("Removed:", removed)
print(inventory)

๐Ÿงฑ Nested Dictionaries

A nested dictionary stores a dictionary inside another dictionary. This is useful for records such as bookings, products, characters or teams.

workshops = {
    "robotics": {"room": "Lab 2", "places": 18, "level": "intermediate"},
    "animation": {"room": "Studio", "places": 12, "level": "beginner"},
    "cyber": {"room": "Lab 4", "places": 16, "level": "advanced"}
}

print(workshops["cyber"]["places"])

for workshop, details in workshops.items():
    print(workshop, details["room"], details["level"])

Pattern: use the outer key first, then the inner key: workshops["cyber"]["places"].

๐Ÿ“„ Word Frequency From a File

A common dictionary task is counting how often each word appears. Clean the text, split it into words, then update a count for each word.

import string

signal_counts = {}

with open("weather_report.txt", "r") as file:
    text = file.read().lower()

for mark in string.punctuation:
    text = text.replace(mark, " ")

words = text.split()

for word in words:
    signal_counts[word] = signal_counts.get(word, 0) + 1

print("Different words:", len(signal_counts))
print("rain:", signal_counts.get("rain", 0))

๐Ÿงฉ Procedures, Functions and Parameters

A procedure performs a named block of steps. A function returns a value. Parameters are variable names in the definition; arguments are the values passed in the call.

def print_receipt(name, price):
    print(name, "costs", price)

def add_tax(price):
    return price * 1.06

total = add_tax(100)
print_receipt("Keyboard", total)

๐ŸŽฏ Scope, Return and Decomposition

Local variables exist inside a function. Global variables exist outside. Good programs decompose a large problem into smaller subroutines with clear jobs.

def is_valid_mark(mark):
    if mark < 0 or mark > 100:
        return False
    return True

mark = int(input("Mark: "))
if is_valid_mark(mark):
    print("Accepted")
else:
    print("Rejected")

๐Ÿ“ž Parameters and Arguments

A parameter is the variable name in the subroutine definition. An argument is the real value sent into the subroutine when it is called.

def announce_train(destination, platform):
    print("Train to", destination)
    print("Platform", platform)

announce_train("Ipoh", 3)
announce_train("Seremban", 7)
Word
In this example
Parameters
destination and platform
Arguments
"Ipoh", 3, "Seremban" and 7

โ†ฉ๏ธ Return Values

A procedure performs actions. A function calculates or chooses a value and sends it back with return. Store the returned value if you need it later.

def delivery_cost(distance_km):
    base = 4
    per_km = 1.5
    return base + distance_km * per_km

short_trip = delivery_cost(3)
long_trip = delivery_cost(12)

print(short_trip)
print(long_trip)

๐Ÿงฎ Using Return Values in Expressions

A function call can be used anywhere its returned value makes sense: in a calculation, comparison, variable assignment, or another function call.

def minutes_to_seconds(minutes):
    return minutes * 60

def total_lesson_seconds(lesson_count, minutes_each):
    return lesson_count * minutes_to_seconds(minutes_each)

total = total_lesson_seconds(5, 45)
print("Total seconds:", total)

if minutes_to_seconds(10) > 500:
    print("Longer than 500 seconds")

๐Ÿšช Early Return

A return statement ends the function immediately. This is useful for validation because you can reject invalid data before the main calculation runs.

def ticket_price(age):
    if age < 0:
        return "Invalid age"
    if age < 12:
        return 8
    if age >= 65:
        return 6
    return 12

print(ticket_price(9))
print(ticket_price(40))
print(ticket_price(-3))

๐Ÿ“ฆ Local Scope and Global Scope

Variables created inside a function are local to that function. Return a new value instead of secretly changing a global value.

temperature = 28

def add_heat(current_temperature, increase):
    new_temperature = current_temperature + increase
    return new_temperature

updated = add_heat(temperature, 4)

print("Original:", temperature)
print("Updated:", updated)

๐Ÿงบ Mutable Arguments

Lists and dictionaries are mutable. If a procedure changes a list or dictionary that was passed in, the original collection changes too.

def record_scan(log, item_code):
    log.append(item_code)

scanned_items = []

record_scan(scanned_items, "BK-104")
record_scan(scanned_items, "USB-210")

print(scanned_items)

๐Ÿงฑ Decomposition Pattern

Decomposition means splitting a program into small named jobs. A good helper should have one clear purpose.

def clean_name(name):
    return name.strip().title()

def create_badge(name, group):
    tidy_name = clean_name(name)
    return tidy_name + " - Group " + group

def print_badge(name, group):
    badge = create_badge(name, group)
    print("[" + badge + "]")

print_badge("  maya  ", "C")
  • clean_name() prepares data.
  • create_badge() builds a value and returns it.
  • print_badge() handles output.

โœ… Validation vs Verification

Validation checks whether data is sensible before processing. Verification checks whether data has been copied or entered accurately.

Check
Purpose
presence
Rejects an empty value.
range
Checks a number is between limits.
length
Checks text has the required number of characters.
type
Checks the value can be treated as a particular data type.
format
Checks the pattern, such as letters followed by digits.

๐Ÿงช Try Common Checks

These are the same kinds of checks used in the validation and verification course: range, length, type, presence, format, visual check and double entry.

Try 85, 101 or -4 for the range check.

๐Ÿ”’ Input Validation Loop

A validation loop keeps asking until the data passes the required check.

mark_text = input("Enter mark 0-100: ")

while not mark_text.isdigit() or int(mark_text) < 0 or int(mark_text) > 100:
    print("Invalid mark")
    mark_text = input("Enter mark 0-100: ")

mark = int(mark_text)
print("Accepted:", mark)

๐Ÿ“Š Searching Algorithms

Linear search checks items one by one. Binary search needs sorted data and repeatedly discards half of the search space.

items = [3, 8, 12, 20, 31]
target = 20
low = 0
high = len(items) - 1
found = False

while low <= high and not found:
    mid = (low + high) // 2
    if items[mid] == target:
        found = True
    elif target < items[mid]:
        high = mid - 1
    else:
        low = mid + 1

print(found)

๐Ÿง  Searching Exam Checklist

Algorithm
What students must say
linear search
Works on unsorted data; checks each item from the start until the target is found or the list ends.
binary search
Requires sorted data; checks the middle item and repeatedly halves the remaining search space.
worst case
Linear search may check every item. Binary search stops after about log2(n) splits.

๐Ÿ”ƒ Sorting and Big O

Sorting algorithms arrange data into order. Complexity describes how work grows as the input size n grows.

Term
Meaning
O(1)
Constant time: work does not grow with the input size.
O(log n)
Logarithmic time: the problem is repeatedly divided.
O(n)
Linear time: one pass through the data.
O(n^2)
Quadratic time: nested passes over the data.

๐Ÿ“ˆ Big O Growth Visualizer

Move n to see how the common complexity classes grow. The numbers are simplified operation counts so the shape is clear; this is about comparing growth, not timing real hardware.

๐Ÿ Python Examples to Try

These are static code examples in this manual, not an embedded editor. Use the Edit button on a snippet to open it in the Python editor and try it there.

O(1) Constant

One direct lookup.

scores = [82, 91, 77, 88]
first = scores[0]
print(first)

O(log n) Binary Search

The search space halves each step.

items = [3, 8, 12, 20, 31, 44, 50, 72]
target = 44
low = 0
high = len(items) - 1
found = False

while low <= high:
    mid = (low + high) // 2
    if items[mid] == target:
        found = True
        break
    elif target < items[mid]:
        high = mid - 1
    else:
        low = mid + 1

print(found)

O(n) Linear Search

One pass through the list.

items = [3, 8, 12, 20, 31, 44, 50, 72]
target = 44
found = False

for item in items:
    if item == target:
        found = True

print(found)

O(n log n) Merge Sort

Split into halves, then merge each level. (Not in A-level)

def merge(left, right):
    result = []
    while len(left) > 0 and len(right) > 0:
        if left[0] < right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
    return result + left + right

def merge_sort(items):
    if len(items) <= 1:
        return items
    mid = len(items) // 2
    left = merge_sort(items[:mid])
    right = merge_sort(items[mid:])
    return merge(left, right)

print(merge_sort([7, 3, 9, 2, 6]))

O(nยฒ) Bubble Sort

Nested comparisons over the data. Note that Big O represents the worst-case scenario, so both Bubble Sort and Insertion Sort share this same O(nยฒ) time complexity.

values = [7, 3, 9, 2, 6]
n = len(values)

for pass_num in range(n):
    for i in range(n - 1):
        if values[i] > values[i + 1]:
            values[i], values[i + 1] = values[i + 1], values[i]

print(values)

O(2โฟ) Naive Fibonacci

Each call branches into two more calls.

def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(8))

O(n!) Permutations

Try every possible ordering.

from itertools import permutations

items = ["A", "B", "C"]

for ordering in permutations(items):
    print(ordering)

๐Ÿซง Interactive Bubble Sort

Bubble sort repeatedly compares neighbouring items and swaps them when they are in the wrong order. Step through the comparisons one at a time.

๐Ÿ’ป Bubble Sort Implementation

Here is a complete Python implementation of the bubble sort algorithm to sort a list of numbers.

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        # Last i elements are already in place
        for j in range(0, n - i - 1):
            # Swap if the element found is greater than the next element
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

numbers = [7, 3, 9, 2, 6]
bubble_sort(numbers)
print("Sorted list:", numbers)

๐Ÿง  Bubble Sort Trace

In exams, students often lose marks because they describe sorting in general instead of the exact pass pattern. Use this trace language.

Step
Action
Why it matters
Compare
Look at two neighbouring items.
Bubble sort only swaps adjacent values.
Swap
If the left item is bigger, swap the pair.
The larger value moves one place to the right.
Pass
Keep comparing until the end of the unsorted section.
After one pass, the largest unsorted value is in its final position.

๐Ÿ“ฅ Interactive Insertion Sort

Insertion sort builds a sorted section on the left. Each new key moves left until it reaches its correct position.

๐Ÿ’ป Insertion Sort Implementation

Here is a complete Python implementation of the insertion sort algorithm to sort a list of numbers.

def insertion_sort(arr):
    # Traverse from 1 to len(arr)
    for i in range(1, len(arr)):
        key = arr[i]
        j = i - 1
        # Move elements of arr[0..i-1], that are greater than key,
        # to one position ahead of their current position
        while j >= 0 and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key

numbers = [7, 3, 9, 2, 6]
insertion_sort(numbers)
print("Sorted list:", numbers)

๐Ÿง  Insertion Sort Trace

Insertion sort grows a sorted section from the left. Each new key is inserted into the correct place inside that section.

Term
Meaning
key
The next value being inserted into the sorted section.
shift
Move larger values one place right to make space for the key.
sorted section
The left part of the list that is already in order.

๐Ÿ“š Stacks

A stack is a Last In, First Out structure. The newest item is always the next item removed.

Operation
Meaning
push
Add an item to the top.
pop
Remove and return the top item.
peek
Look at the top item without removing it.

๐ŸŽ›๏ธ Stack Visual

Watch how every operation happens at the top of the stack.

๐Ÿ’ป Stack Implementations

Use the list-based version to learn the behaviour quickly. Use the OOP version when the stack should protect its own data and expose named methods.

List-based stack
stack = []

stack.append("scan")
stack.append("pack")
stack.append("label")

top_item = stack[-1]
removed = stack.pop()

print("Top was:", top_item)
print("Removed:", removed)
print(stack)
OOP stack class
class Stack:
    def __init__(self, limit):
        self.__items = []
        self.__limit = limit

    def push(self, item):
        if len(self.__items) == self.__limit:
            return "Overflow"
        self.__items.append(item)
        return "Pushed"

    def pop(self):
        if len(self.__items) == 0:
            return "Underflow"
        return self.__items.pop()

    def peek(self):
        if len(self.__items) == 0:
            return None
        return self.__items[-1]

work = Stack(3)
work.push("scan")
work.push("pack")
print(work.peek())
print(work.pop())

๐Ÿง  Stack Exam Checklist

  • Use top to track the most recent item.
  • Check for overflow before pushing onto a fixed-size stack.
  • Check for underflow before popping from an empty stack.
  • Remember that pop() removes and returns an item; peek() only reads it.

๐Ÿšถ Queues

A queue is a First In, First Out structure. New items join at the rear, and old items leave from the front.

Operation
Meaning
enqueue
Add an item to the rear of the queue.
dequeue
Remove and return the item at the front.
front/rear
Pointers or indexes used to track the next item out and newest item in.

๐ŸŽ›๏ธ Queue Visual

See the difference between the front and rear of the queue.

๐Ÿ’ป Queue Implementations

The list-based version is short and readable. The OOP version makes the queue behaviour explicit through enqueue(), dequeue() and front().

List-based queue
queue = []

queue.append("ticket-101")
queue.append("ticket-102")
queue.append("ticket-103")

next_item = queue[0]
served = queue.pop(0)

print("Next was:", next_item)
print("Served:", served)
print(queue)
OOP queue class
class Queue:
    def __init__(self, limit):
        self.__items = []
        self.__limit = limit

    def enqueue(self, item):
        if len(self.__items) == self.__limit:
            return "Overflow"
        self.__items.append(item)
        return "Enqueued"

    def dequeue(self):
        if len(self.__items) == 0:
            return "Underflow"
        return self.__items.pop(0)

    def front(self):
        if len(self.__items) == 0:
            return None
        return self.__items[0]

service = Queue(3)
service.enqueue("ticket-101")
service.enqueue("ticket-102")
print(service.front())
print(service.dequeue())

๐Ÿง  Queue Pointer Checklist

  • front points to the next item to leave the queue.
  • rear points to the newest item added to the queue.
  • In a circular queue, pointers wrap back to index 0 after the final position.
  • Overflow means enqueueing into a full fixed-size queue; underflow means dequeueing from an empty queue.

๐Ÿ”— Linked Lists

A linked list is made from nodes. Each node stores data and a pointer to the next node. A null pointer marks the end.

List-based linked list using indexes
data = ["badge", "lanyard", "map"]
next_index = [1, 2, -1]
start = 0

current = start
while current != -1:
    print(data[current])
    current = next_index[current]
OOP linked list using nodes
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

start = Node("badge")
start.next = Node("lanyard")
start.next.next = Node("map")

current = start
while current is not None:
    print(current.data)
    current = current.next

Vocabulary: node, pointer, start pointer, null pointer, free list, traversal.

๐ŸŽ›๏ธ Linked List Visual

Nodes do not need to sit next to each other in memory. Each node points to the next node.

๐Ÿง  Linked List Operations

Operation
Key idea
Traverse
Start at the first node and follow each next pointer until None.
Insert
Create a new node, update its pointer, then update the previous node or start pointer.
Delete
Bypass the removed node by changing the previous node's pointer.
Search
Compare each node's data while traversing; stop when the value is found or the list ends.

๐ŸŒ€ Recursion

Recursion is when a function solves a problem by calling itself. Every recursive algorithm needs a base case to stop and a general case that moves closer to the base case.

def factorial(n):
    if n == 0:
        return 1          # base case
    return n * factorial(n - 1)  # general case

print(factorial(5))

๐ŸŽฌ Factorial Call Stack Animation

This shows factorial(4) in two phases: first Python creates new function calls until it reaches the base case, then each call returns a value back to the previous call.

def factorial(n):
    if n == 0:
        return 1
    return n * factorial(n - 1)

print(factorial(4))

Call stack

Return values

๐Ÿ“š Call Stack and Unwinding

Each recursive call creates a stack frame storing parameters, local variables and a return address. When the base case is reached, the calls return in reverse order. This is called unwinding.

๐Ÿง  Recursion Trace Pattern

When tracing recursion, write down each call until the base case, then work backwards as the calls return.

factorial(3)
= 3 * factorial(2)
= 3 * 2 * factorial(1)
= 3 * 2 * 1 * factorial(0)
= 3 * 2 * 1 * 1
= 6

๐Ÿ›๏ธ Introduction to Object-Oriented Programming

Object-oriented programming is a way to organise a program around objects. An object keeps related data and the actions that work on that data together. This is useful when a program has many similar things to track, such as accounts, sensors, bookings, players, products or tree nodes.

Without OOP, related variables can become scattered through a program. With OOP, a class describes the pattern once, then each object stores its own values and can use the same methods.

Loose variables

sensor1_location = "Lab"
sensor1_reading = 21.4
sensor2_location = "Roof"
sensor2_reading = 33.8
sensor3_location = "Library"
sensor3_reading = 24.1
โ†’

Class and objects

class Sensor
attributes: location, reading
methods: update(), is_hot()
lab
location: "Lab"
reading: 21.4
roof
location: "Roof"
reading: 33.8
library
location: "Library"
reading: 24.1
OOP idea
Why it helps
Class
Defines the shared structure once.
Object
Stores one real item's data using that structure.
Attribute
Keeps a value inside the object it belongs to.
Method
Keeps behaviour close to the data it changes or reads.

๐Ÿงฑ Classes, Objects and Instances

A class is a blueprint. An object is one instance made from that blueprint. Each object can store different values in its attributes.

class Sensor:
    def __init__(self, location, reading):
        self.location = location
        self.reading = reading

lab = Sensor("Lab", 21.4)
roof = Sensor("Roof", 33.8)

print(lab.location, lab.reading)
print(roof.location, roof.reading)

โš™๏ธ Constructors, Attributes and self

The constructor __init__ runs when a new object is created. self means the current object, so self.reading belongs to one specific sensor object.

Code
Meaning
class Sensor:
Defines a new type of object.
__init__
Sets up a new object when it is created.
self.location
An attribute stored inside the current object.
Sensor("Lab", 21.4)
Creates an instance and passes arguments to the constructor.

๐Ÿ› ๏ธ Methods: Object Behaviour

A method is a function inside a class. It normally uses self so it can read or change that object's attributes.

class Sensor:
    def __init__(self, location, reading):
        self.location = location
        self.reading = reading

    def update(self, new_reading):
        self.reading = new_reading

    def is_hot(self):
        return self.reading >= 30

roof = Sensor("Roof", 28.5)
roof.update(31.2)
print(roof.is_hot())

๐Ÿ”’ Encapsulation, Getters and Setters

Encapsulation keeps data and the methods that control that data together. Getters read a value. Setters update a value, often with validation.

class Locker:
    def __init__(self, number):
        self.__number = number
        self.__open = False

    def get_number(self):
        return self.__number

    def set_open(self, open_now):
        if open_now == True or open_now == False:
            self.__open = open_now

    def is_open(self):
        return self.__open

locker = Locker(42)
locker.set_open(True)
print(locker.get_number(), locker.is_open())

๐Ÿ‘ฅ Objects in Lists

Programs often create many objects from one class and store them in a list. A loop can then process every object in the collection.

class Sensor:
    def __init__(self, location, reading):
        self.location = location
        self.reading = reading

sensors = [
    Sensor("Lab", 21.4),
    Sensor("Roof", 33.8),
    Sensor("Library", 24.1)
]

for sensor in sensors:
    print(sensor.location, sensor.reading)

๐Ÿ“„ Loading File Records into Objects

A common OOP pattern is to read each line from a file, create one object from that line, and append the object to a list. The list stores the collection; the class stores the data and behaviour for one item.

class Plant:
    def __init__(self, name, sunlight, water_ml):
        self.name = name
        self.sunlight = sunlight
        self.water_ml = int(water_ml)

    def needs_large_drink(self):
        return self.water_ml >= 300

plants = []

with open("greenhouse.txt", "r") as file:
    for line in file:
        line = line.strip()
        if line != "":
            parts = line.split(",")
            plant = Plant(parts[0], parts[1], parts[2])
            plants.append(plant)

for plant in plants:
    if plant.needs_large_drink():
        print(plant.name, "needs", plant.water_ml, "ml")

โœจ Dunder Methods: Printing Objects Clearly

Dunder methods have double underscores before and after their names. Python calls them automatically for special behaviour. __str__ is the most useful starting point because it controls what appears when you print an object.

class Plant:
    def __init__(self, name, sunlight, water_ml):
        self.name = name
        self.sunlight = sunlight
        self.water_ml = water_ml

    def __str__(self):
        return self.name + " needs " + str(self.water_ml) + " ml of water"

    def __repr__(self):
        return "Plant(" + repr(self.name) + ", " + repr(self.sunlight) + ", " + repr(self.water_ml) + ")"

fern = Plant("Fern", "shade", 180)

print(fern)
print([fern])
Method
Use
__str__
Human-friendly text for print(object).
__repr__
Developer-friendly text, also used when the object appears inside a list.
__eq__
Controls equality checks such as a == b.
__lt__
Controls less-than comparisons, useful when sorting objects.

๐Ÿงฌ Inheritance

Inheritance lets a subclass reuse attributes and methods from a superclass. Use it when the subclass really is a specialised type of the superclass.

class Vehicle:
    def __init__(self, identifier):
        self.identifier = identifier

    def describe(self):
        print("Vehicle:", self.identifier)

class ElectricBus(Vehicle):
    def __init__(self, identifier, battery_percent):
        super().__init__(identifier)
        self.battery_percent = battery_percent

bus = ElectricBus("BUS-18", 76)
bus.describe()
print(bus.battery_percent)

๐ŸŽญ Polymorphism

Polymorphism means different classes can use the same method name, but each class can perform a different action.

class EmailAlert:
    def send(self):
        print("Sending email")

class ScreenAlert:
    def send(self):
        print("Showing screen message")

alerts = [EmailAlert(), ScreenAlert()]

for alert in alerts:
    alert.send()

๐ŸŽฏ Abstraction and Aggregation

Abstraction means choosing the important details and ignoring unnecessary detail. Aggregation means one object stores or uses another object.

class Battery:
    def __init__(self, percent):
        self.percent = percent

class Tablet:
    def __init__(self, name, battery):
        self.name = name
        self.battery = battery

main_battery = Battery(88)
tablet = Tablet("Art tablet", main_battery)

print(tablet.name, tablet.battery.percent)

๐Ÿ“š OOP ADTs: Stacks, Queues and Linked Lists

Abstract data types describe behaviour, not just storage. An OOP implementation hides the internal list or pointers behind methods such as push(), pop(), enqueue(), dequeue(), insert() and search().

ADT
OOP idea
Stack
A class can keep a private list and expose only push(), pop() and peek().
Queue
A class can control front/rear pointers and hide circular wraparound logic.
Linked list
A Node class stores data and the next pointer; a list class controls the start pointer.

๐ŸŒณ Binary Trees as OOP Structures

Binary trees are a natural bridge between OOP and recursion. A node object stores data plus left and right references. Insert and search methods usually call themselves on the left or right subtree.

class TreeNode:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

root = TreeNode(50)
root.left = TreeNode(25)
root.right = TreeNode(75)

print(root.value)
print(root.left.value)
print(root.right.value)

Pattern: smaller values move to the left reference; larger values move to the right reference.

๐ŸŒณ Binary Search Trees

A binary tree node can have up to two children. In a binary search tree, smaller values go left and larger values go right.

class Node:
    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

root = Node(50)
root.left = Node(25)
root.right = Node(75)

This is the same object pattern used in the OOP section: each node stores a value and references to child nodes.

๐Ÿ”Ž Traversal and Tree Search

In-order traversal visits left subtree, current node, then right subtree. In a binary search tree this outputs values in sorted order.

def inorder(node):
    if node is not None:
        inorder(node.left)
        print(node.value)
        inorder(node.right)

๐Ÿง  Traversal Order

Traversal
Visit order
In-order
Left subtree, node, right subtree. In a binary search tree, this gives sorted output.
Pre-order
Node, left subtree, right subtree. Useful when copying or saving a tree structure.
Post-order
Left subtree, right subtree, node. Useful when deleting or evaluating from the leaves up.

๐ŸŒณ Binary Tree Visualiser

Insert and search values to see the exact path through a binary search tree. Smaller values move left; larger or equal values move right.

๐Ÿ› A Student's Guide to Debugging Python

Debugging means finding and fixing problems in your code. Bugs are normal. Every programmer creates them. The important skill is learning how to find them calmly and carefully.

๐Ÿงญ Debugging Flowchart

Use this routine whenever a program crashes, gives the wrong answer, repeats forever, or appears to do nothing.

1 Stop and read What actually happened? Error, wrong answer, early stop, endless loop, or nothing?
2 Find the clue Read the error type, line number, variable name, or the first wrong output.
3 Check simple things Look for spelling, brackets, speech marks, colons, capital letters, and indentation.
4 Trace the code Say what each line does. Add temporary print() statements if needed.
5 Test one idea Change one thing, use simple test values, then run the program again.
6 Repeat or ask If you are still stuck after five focused minutes, ask with the code, error, and expected result.

๐Ÿ”Ž Read the Error Message

Python error messages are clues. The most useful information is often near the bottom: the error type, the line number, and the name of the variable or command mentioned.

NameError: name 'scroe' is not defined

This suggests that a variable name has been typed incorrectly. Check the line Python mentions and also the line just before it.

if score > 10
    print("You win!")

Python may point at the print() line, but the real problem is the missing colon:

if score > 10:
    print("You win!")

โœ… Check the Simple Things First

Typing

  • Are all brackets closed?
  • Are all speech marks closed?
  • Are variable names spelt correctly?

Python syntax

  • Is there a colon after if, for, while or def?
  • Is the indentation correct?
  • Have you used the correct capital letters?

Logic

  • What answer did you expect?
  • What answer did you actually get?
  • Where does the value first become wrong?

Python is case-sensitive. score, Score and SCORE are three different variable names.

๐Ÿ–จ๏ธ Use print() as a Clue

Add temporary print() statements to check what values are stored and which parts of the program are running.

score = 10
bonus = 5

print("Score is", score)
print("Bonus is", bonus)

total = score + bonus

print("Total is", total)

Remove the extra print statements after fixing the bug.

๐Ÿงช Test One Small Part

Large programs are easier to debug when you test one small section at a time. Use simple values where you already know the correct answer.

price = 5
quantity = 3

total = price * quantity
print(total)  # Should be 15
  • For an average calculator, try 10, 10, 10. The average should be 10.
  • For a multiplication program, try 2 and 5. The answer should be 10.
  • For an age checker, test the edge cases: 17, 18, and 19.

๐Ÿง  Common Debugging Situations

Wrong answer

This is a logic error. Work out the correct answer yourself, then follow the values through the program.

Loop will not stop

Check whether the loop variable changes, whether the condition can become false, and whether the update is inside the loop.

List error

Remember that list positions begin at 0. The last item in a three-item list is at position 2.

animals = ["tiger", "panda", "otter"]

print(animals[2])  # otter
print(len(animals))  # 3

๐Ÿ™‹ How to Ask for Help

Do not just say:

It does not work.

Give useful information instead:

I expected the program to print 15, but it prints 8.
There is no error message.
I think the problem is in the total calculation.
  • Include the code.
  • Include the full error message.
  • Explain what you expected.
  • Explain what actually happened.
  • Say what you already tried.

๐Ÿ“‹ Debugging Checklist

  • What did I expect to happen?
  • What actually happened?
  • Is there an error message?
  • Which line is mentioned? Did I check the line before it?
  • Are spelling, brackets, speech marks, colons and indentation correct?
  • Are my variables the correct type?
  • Does my loop eventually stop?
  • Are my list positions valid?
  • Can I print the values?
  • Have I changed only one thing at a time?

๐Ÿ”— Further Learning & Reference

If you want to dive deeper into Python programming, these highly recommended tutorials and documentations are excellent places to expand your knowledge:

๐ŸŽ“ F-String Comic Academy

Learn how to format strings dynamically using variables and expressions in a 10-level beginner comic academy course featuring Byte and Pixel.

๐Ÿ“š W3Schools Python Tutorial

An exceptionally beginner-friendly, structured guide with simple explanations and lots of small interactive code examples.

๐Ÿ Official Python Documentation

The definitive source for Python standard libraries, syntax rules, and official explanations. Great for looking up specific modules or features.

๐Ÿ‘‘ Real Python Tutorials

A collection of high-quality, practical articles and step-by-step guides covering core concepts and real-world Python applications.

๐Ÿ” Python Search Engines

Looking for a specific function, library, or error solution? Use these specialized search portals to search directly within trusted python documentation:

Python Official Search

Search official guides, modules, and built-in function references.

W3Schools Python Search

Search user-friendly tutorials and code examples on W3Schools.

๐Ÿ” Search Results

Results matching your query will appear here.