B2.1 Programming Fundamentals
This article has not been finished yet and most likely contains mistakes or misses extrmly important stuff.
- B2.1.1: Variables and Data Types
- B2.1.2: Substrings
- B2.1.3: Exception Handling
- B2.1.4: Debugging Techniques
All examples use Python.
B2.1.1: Variables and Data Types
Variables
A variable is a named location in memory that holds data. In Python you create one just by assigning a value to it. You do not have to state the type, Python works it out.
my_number = 5
name = "Ivan"
You can add an optional type hint to make the intended type clear. Python does not enforce it, it just makes code easier to read.
my_number: int = 5
Declaring your main variables near the top of a program makes it easier to read and debug.
Data Types
The data type decides what can be stored in a variable and what you can do with it.
| Type | Meaning | Python example |
|---|---|---|
| Boolean | True or False | is_vegan: bool = False |
| Integer | Whole number | glasses: int = 0 |
| Float | Number with a decimal point | price: float = 7.99 |
| String | Sequence of characters | word: str = "hello" |
| Char | A single character | No separate type. Use a string of length 1: initial: str = "a" |
Useful tools
type(x)tells you the type of a variable.- Casting converts between types:
int("5")gives 5,str(5)gives "5",float("7.99")gives 7.99. input()always returns a string. Cast it if you need a number:age = int(input("Age: "))
Global and Local Variables
- Global variable: created outside any function. It can be read anywhere in the program.
- Local variable: created inside a function. It only exists while that function runs. Using it outside gives a
NameError. - To change a global variable inside a function you must use the
globalkeyword.
count = 0 # global
def add_one():
global count # lets the function change the global
count = count + 1
def greet():
message = "hi" # local, only exists inside greet()
print(message)
add_one()
greet()
print(count) # 1
print(message) # NameError: message is not defined
Local variables are safer because nothing outside the function can accidentally change them. Use globals sparingly.
Why Data Types Matter
- Choosing a type is a decision about what data you accept and how precise it is. Storing age as an integer gives only years. A float could capture months too.
- Python does not need types declared upfront, which makes it quick to write but slower to run than languages that do. That is why very low latency systems like trading platforms often use faster compiled languages, while Python is popular for trying out ideas quickly.
Practice Exercises
Choose and justify a data type for each:
- A telephone number
- Whether a person is vegan
- A password
- The cost of an item
- The number of glasses of water drunk in a day
- A person's middle initial
- String. It can start with 0 or +, may contain spaces, and you never do maths with it. As an integer the leading zero would be lost.
- Boolean. Only two possible values: True or False.
- String. Mix of letters, numbers and symbols.
- Float. Needs decimal places for cents.
- Integer. You count whole glasses.
- String of length 1. Python has no char type.
What does this print, and why?
score = 10
def update():
score = 50
print(score)
update()
print(score)
It prints 50 then 10.
Inside update(), score = 50 creates a new local variable. It does not touch the global one. To change the global you would need global score at the start of the function.
input()always gives a string. Forgetting to cast is one of the most common bugs.- Phone numbers are strings, not integers. This comes up a lot.
- Local variables disappear when the function ends. Globals need the
globalkeyword to be changed inside a function.
B2.1.2: Substrings
Strings and Indexes
A string is a sequence of characters. Every character has an index, starting at 0. Negative indexes count from the end.
| Character | O | l | d | (space) | T | o | w | n |
|---|---|---|---|---|---|---|---|---|
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| Negative | −8 | −7 | −6 | −5 | −4 | −3 | −2 | −1 |
String Operations
All examples use s = "Old Town".
| Operation | Result | What it does |
|---|---|---|
s[4] | "T" | Character at an index |
s[3:6] | " To" | Slice from index 3 up to, but not including, 6 |
s[:3] | "Old" | Slice from the start |
s[4:] | "Town" | Slice to the end |
len(s) | 8 | Number of characters |
"Old" in s | True | Checks if a substring exists |
s.find("T") | 4 | Index of the first match. Returns −1 if not found |
s.rfind("own") | 5 | Index of the last match |
s.replace("Old", "New") | "New Town" | Replaces every match |
s.split() | ["Old", "Town"] | Splits on spaces into a list |
s.upper() / s.lower() | "OLD TOWN" / "old town" | Changes case |
s + " is old" | "Old Town is old" | Concatenation: joining strings |
Things to remember
- Strings are immutable. Methods like
replace()return a new string, they do not change the original. Save the result:s = s.replace("Old", "New") - A slice's end index is not included.
s[3:6]gives indexes 3, 4 and 5. - You can only concatenate strings with strings.
"Age: " + 17crashes. Use"Age: " + str(17). - Python has many more string methods. The full list is in the Python documentation, which describes the language's API (the set of methods and rules you can use).
Most programs take user input as strings, so being comfortable with string handling makes your programs much more useful.
Practice Exercises
With s = "Computer Science", what does each give?
s[0]s[9:16]s.find("S")len(s)s.split()
"C""Science"916["Computer", "Science"]
Write a program that creates a starting password by taking the first three letters of a person's surname, making them lowercase, and adding special characters and the length of the surname.
surname = input("Enter your surname: ")
password = surname[:3].lower() + "#" + str(len(surname)) + "!"
print("Your starting password is", password)
# Input: Kruchinin
# Output: Your starting password is kru#9!
Note the str() around len(surname). Without it you would be adding a string to an integer and the program would crash.
- Indexes start at 0. The slice end is never included.
find()returns −1 when nothing is found. It does not crash.- Strings cannot be changed in place. Always store the result of a string method.
B2.1.3: Exception Handling
What is Exception Handling?
An exception is unexpected behaviour while a program runs. If it is not handled, the program crashes. Exception handling lets the program deal with the problem and carry on.
Three Points of Failure
| Failure | What happens | How to prevent it |
|---|---|---|
| Unexpected input | The user enters something the program was not expecting, like letters when a number was needed. | Validation: range checks, dropdown menus with only valid options, checking required content (an @ in an email). |
| Resource unavailable | A file, library, sensor or actuator the program needs is missing or was not imported properly. | Wrap access in try/except and give a clear message instead of crashing. |
| Logic errors | The code runs but gives wrong results. Causes: wrong brackets in calculations, reusing a variable name, infinite loops, misused Boolean operators. | Thorough testing with lots of different data, including edge cases. |
try, except, finally
Risky code goes in the try block. If it fails, the except block runs instead of the program crashing.
pets = ["Ben", "Leo", "Zeppo"]
try:
print(pets[5])
except IndexError:
print("An index out of range error has occurred")
# Output: An index out of range error has occurred
A finally block always runs, whether there was an error or not. Useful for cleanup like closing files.
try:
print(pets[5])
except IndexError:
print("An error has occurred")
finally:
print("The program has ended safely")
# Output:
# An error has occurred
# The program has ended safely
Common exception types
| Exception | When it happens |
|---|---|
ValueError | Right type but wrong value, e.g. int("abc") |
IndexError | List index out of range |
KeyError | Dictionary key does not exist |
ZeroDivisionError | Dividing by zero |
TypeError | Wrong type, e.g. "a" + 1 |
FileNotFoundError | Opening a file that does not exist |
NameError | Using a variable that does not exist |
Catching a specific exception is better than a bare except:, because you know exactly what went wrong and you do not hide other bugs.
Input Validation Example
This keeps asking until the user enters a valid age. It handles both wrong type and wrong range.
while True:
try:
age = int(input("Enter your age: "))
if 0 <= age <= 120:
break
print("Age must be between 0 and 120")
except ValueError:
print("Please enter a whole number")
print("Age saved:", age)
Practice Exercises
try:
x = int("abc")
print("Converted")
except ValueError:
print("Not a number")
finally:
print("Done")
Not a number
Done
int("abc") raises a ValueError, so "Converted" is never printed. The finally block always runs.
Write a program that asks for two numbers and prints the first divided by the second. It must not crash if the user types letters or enters 0 as the second number.
try:
a = float(input("First number: "))
b = float(input("Second number: "))
print("Result:", a / b)
except ValueError:
print("Please enter numbers only")
except ZeroDivisionError:
print("You cannot divide by zero")
Two separate except blocks give a clear message for each problem.
You are building a program for teachers to store student data. What information would they store, and what validation or exception handling would it need?
Many valid answers. For example:
- Data: name (string), student ID (string), year group (integer), grades (list of integers), email (string).
- Validation: grades between 1 and 7, year group from a dropdown, email must contain @, ID must be a set length.
- Exception handling: handle the data file not being found, catch non numeric grades with
ValueError, usefinallyto close the file safely.
- try holds the risky code, except handles the error, finally always runs.
- Know the three points of failure: unexpected input, unavailable resources, logic errors.
- Logic errors do not raise exceptions. Only testing finds them.
B2.1.4: Debugging Techniques
What is Debugging?
Using tools and techniques to find and fix errors in your code. Always know what inputs your code takes and what output it should give. If the results are wrong, use these techniques to track down why. You will use all of them in your IA.
1. Trace Tables
Track the value of every variable at each step to check it changes the way you expect. Example: counting warm days (over 20°C).
temperature = [23.7, 19.9, 23.8, 18.8, 12.5, 24.0]
warm_days = 0
for temp in temperature:
if temp > 20:
warm_days = warm_days + 1
print("The number of warm days this month:", warm_days)
| Iteration | temp | temp > 20? | warm_days | Output |
|---|---|---|---|---|
| 0 | 23.7 | True | 1 | |
| 1 | 19.9 | False | 1 | |
| 2 | 23.8 | True | 2 | |
| 3 | 18.8 | False | 2 | |
| 4 | 12.5 | False | 2 | |
| 5 | 24.0 | True | 3 | 3 |
19,9 instead of 19.9 creates two separate items (19 and 9), which silently breaks the result. A trace table would catch this straight away.
2. Print Statements
Add print lines to show which loop, iteration or branch the program is in and what the variables hold at that moment. This shows whether the program runs the way you expect. Example: a selection sort with debug prints.
to_sort = [4, 56, 23, 2938, 29, 485, 2]
for i in range(len(to_sort) - 1):
min_index = i
print("Outer loop, i =", i)
for j in range(i + 1, len(to_sort)):
print(" Inner loop, j =", j)
if to_sort[j] < to_sort[min_index]:
min_index = j
print(" New smallest at index", min_index)
to_sort[i], to_sort[min_index] = to_sort[min_index], to_sort[i]
print(to_sort) # [2, 4, 23, 29, 56, 485, 2938]
The output is a running log of exactly what the code is doing. Remove the debug prints once the bug is fixed.
3. Breakpoints
A breakpoint pauses the program at a chosen line so you can inspect every variable at that moment. In most IDEs (VS Code, PyCharm, Thonny) you click in the margin next to a line number to set one. You can also add breakpoint() directly in Python code.
min_index = j lets you see the values each time a new smallest item is found.
4. Step by Step Execution
The IDE runs the code one line at a time. It highlights the current line and shows a live table of all variable values.
- Step over: run the current line, including any function it calls, as one step.
- Step into: go inside the function being called and run it line by line.
- Step out: finish the current function and return to where it was called.
Practice Exercises
nums = [3, 8, 1, 6]
total = 0
for n in nums:
if n > 2:
total = total + n
print(total)
Draw a trace table with columns n, n > 2?, total and Output.
| n | n > 2? | total | Output |
|---|---|---|---|
| 3 | True | 3 | |
| 8 | True | 11 | |
| 1 | False | 11 | |
| 6 | True | 17 | 17 |
This should print the average of the list, but it prints the wrong number. Find the bug.
marks = [6, 7, 5, 4]
total = 0
for m in marks:
total = total + m
average = total / len(marks)
print(average)
The print (and the average calculation) are indented inside the loop, so it prints a running value four times instead of one final average. Print statements make this obvious: you see 1.5, 3.25, 4.5, 5.5.
marks = [6, 7, 5, 4]
total = 0
for m in marks:
total = total + m
average = total / len(marks)
print(average) # 5.5- Know all four techniques by name: trace tables, print statements, breakpoints, step by step execution.
- Trace tables: write a new row every time a variable changes, and check the last pass of the loop carefully.
- In a selection sort using
<, the loop is finding the smallest value, so the list is sorted ascending. Read the comparison, not just the variable name.