✦ New: Free website & AI check for UK small businesses · Book a 20-minute call →
Learning notes · Python · Part 1

Python notes, part 1: fundamentals and problem solving

I am learning Python on a 4 to 6 week course and sharing my notes as I go. Part 1 covers the building blocks every program needs, with simple diagrams, code you can run and a small practice project.

⏱ 20 min read✍ Syed Rizwan AhmedUpdated 8 Nov 2026
About these notes: I am learning Python on a 4 to 6 week course and writing these notes to understand it better. I share them so other beginners can learn with me. They follow my class, written in my own words, and every code example has been run. If you spot a mistake, please tell me.

How to use these notes

1

Read the summary first

Get the big picture before the details.

2

Learn each idea in three parts

The theory, a practical example, and why it matters.

3

Run every example yourself

Copy the code into Python on your own computer and run it. Change a value and see what happens.

4

Test yourself at the end

Answer the quick recall questions before you open the answers.

Summary

This class uses Python as a tool to introduce the way programmers think and the habits that make code good. We learned that:

  • Programming is not just writing code. It is solving a problem logically.
  • Every piece of software starts with understanding the problem and goes all the way to testing and improving it.
  • Functions, variables, conditionals and loops are the basic building blocks of programming.
  • Exceptions and unit tests make code strong and reliable.
  • Libraries, file input/output and regular expressions handle data efficiently.
  • Object-oriented programming is a way to model real-world things in code.

Why this lesson matters

Software is now part of almost everything we do. To be a good programmer, knowing a language's syntax is not enough. You need to break a big problem into small, manageable parts and solve them with clear thinking and organised code.

If you skip the basics…What happens
Software that does not workCode that gives wrong results frustrates users and costs the business.
Unsafe, unstable systemsWithout error handling and testing, software can crash, lose data or open security holes.
Code nobody can maintainCode written without planning and modules becomes impossible to understand and fix over time. This is called technical debt.
Wasted time and moneyWithout the right method, the same problems get solved again and again, so development takes longer and costs more.

The goal: to give learners the thinking tools and skills to write code that is reliable, maintainable and ready for real-world problems.

From problem to software

Every software project goes through these stages:

Flowchart of ten stages from understanding the problem to delivering software
The ten-stage cycle. If the problem is not solved, go back to step 1.

Questions to ask before you start

Business and user questionsTechnical questions
Who will use this software?Which language and tools suit it best?
Which problem does it solve, or which need does it meet?How will the data be stored and organised?
What are its main functions?Do we need an outside library or framework?
How will users interact with it?What could go wrong, including unusual cases, and how will we handle it?
How will we measure success?How will the code stay fast as it grows?
What value does it create?How will we test and verify the code?
Edge cases: unusual inputs people do not usually think of, such as an empty name, a negative age or dividing by zero. A good programmer plans for them in advance.

The building blocks

Functions, variables, conditionals and loops are the alphabet of a programming language. You cannot write a useful program without them.

Functions

Idea: a function is a block of code that does one specific job. Functions split a program into small, reusable parts, which makes code easier to understand and look after.

A function as a machine: length 5 and width 4 go in, 20 comes out
A function is like a machine: inputs go in, the work happens, a result comes out.

Python — functions

def calculate_area(length, width):
    return length * width

def greet(name):
    print(f"Hello, {name}!")

print(calculate_area(5, 4))   # 20
greet("Ali")                  # Hello, Ali!

Why it matters: functions stop you repeating code, split the program into parts and help teams work together, because each developer can work on their own part. They are the first step in solving a big problem in small pieces.

Variables

Idea: variables are named places in the computer's memory that hold data for a while. The name gives you easy access to the data.

Three labelled boxes: name holds Ali, age holds 30, is_student holds True
A variable is like a labelled box: the name on top, the value inside.

Python — variables

name = "Ali"          # a string variable
age = 30              # an integer variable
is_student = True     # a boolean variable

Why it matters: variables let a program change data while it runs, keep results and use information flexibly. Think of labelled jars in a kitchen: the jar says "sugar" and holds sugar. You pick it up by name, and you can change what is inside.

Conditionals (if / else)

Idea: conditionals let a program make decisions. A part of the code runs only if a condition is true or false.

Decision diagram: if score is 60 or more the result is Pass, otherwise Fail
If the condition is true, one path runs; if not, the other.

Python — if / else

score = 85
if score >= 60:
    print("Pass")
else:
    print("Fail")

Why it matters: without conditionals a program runs in a straight line and cannot react to real-world situations. They make programs respond to what is happening.

Loops (for / while)

Idea: loops run a block of code again and again, until a condition is met or every item in a collection is done.

A for loop with range(5) printing 0, 1, 2, 3 and 4, then stopping
A loop repeats the same job a set number of times, then stops.

Python — for and while loops

# For loop: counts from 0 to 4
for i in range(5):
    print(i)

# While loop
count = 0
while count < 3:
    print("Hello")
    count += 1

Why it matters: loops automate repeated work, save time, shorten code and help process large amounts of data.

Which loop? Use a for loop when you know how many times to repeat. Use a while loop when repeating depends on a condition.

Making code strong: exceptions and unit tests

Writing code is not enough. Code should be reliable and keep working even when things go wrong.

Exceptions

Idea: exceptions are a way to handle unexpected errors while a program runs. They stop the program crashing and deal with the problem gracefully.

Dividing 10 by 0 crashes without try/except, but shows a friendly message with it
Exception handling is a safety net: the error is caught and the program does not fall over.

Example: dividing by zero, or trying to open a file that does not exist.

Python — try / except / finally

try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero!")
except Exception as e:
    print(f"An error occurred: {e}")
finally:
    print("This will always run")

Why it matters: this is defensive programming. It keeps software stable with unexpected input, and the user sees a simple message instead of a crash.

How it reads: the try part holds code that might fail, except catches the error, and finally always runs, error or not.

Unit tests

Idea: unit tests check the smallest parts of the code, such as single functions or classes, to make sure each one works. They run automatically.

A function add(a, b) tested with positive, negative and zero values, all passing
Testing one function with positive, negative and zero values.

Example: for a function that adds two numbers, write tests that check the answer is right for positive numbers, negative numbers and zero.

  • Find bugs early: mistakes are caught at the start of development.
  • Stop old features breaking: after a change, tests show if you broke something that used to work.
  • Document the code: good tests show what the code is meant to do.
  • Improve safely: you can rewrite code with confidence because the tests confirm it still works.

Building in modules: libraries and files

Reusing code and handling data well are the foundation of modern programming.

Libraries

Idea: libraries are collections of ready-made code, written by others or by you, that you can use again in your projects.

A bookshelf of Python libraries: math, random, re, os, Flask, Django
A library is like a bookshelf: take what is ready and use it.

Example: math for maths such as sine and cosine, random for random numbers, and Django or Flask for web development.

Python — using libraries

import math
import random

print(math.sqrt(16))           # 4.0
print(random.randint(1, 6))    # a random dice number

Why it matters: do not reinvent the wheel. Libraries save time and effort, so you can focus on the problems that are unique to your project.

File input and output

Idea: file I/O means writing data to files on the disk and reading it back, instead of keeping it only in memory.

Memory is temporary and lost on close; a file on disk is permanent. Write with
Memory is temporary; a file stays after the program closes.

Python — write and read a file

# Writing to a file
with open("my_data.txt", "w") as file:
    file.write("Hello World!\n")
    file.write("This is my first I/O file.\n")

# Reading from a file
with open("my_data.txt", "r") as file:
    content = file.read()
    print(content)

Why it matters: it keeps data safe after the program closes. Databases, log files and settings files all depend on this.

Checking and extracting data: regular expressions

Idea: regular expressions, or regex, are a powerful language for describing patterns. They are used to find, match and change specific patterns in text.

Email addresses pass through a regex filter: valid ones come out True, invalid ones False
Regex works like a sieve: data with the right shape passes, the rest is stopped.

Example: checking an email address has the right shape, finding phone numbers, or pulling dates out of a large text file.

Why it matters: it makes sure what the user typed is what you expect, and it pulls information out of large data sets quickly.

Modelling the real world: object-oriented programming

There are different styles, or paradigms, for organising code. Object-oriented programming (OOP) models real-world things in code. Everything is built around objects, which hold both data and the functions that work on that data.

A Car class blueprint with colour and speed, used to create a red car and a blue car
A class is a blueprint; every car made from it is a separate object.

Python — a simple class

class Car:
    def __init__(self, color, speed):
        self.color = color      # attribute
        self.speed = speed      # attribute

    def accelerate(self):       # method
        self.speed += 10

    def brake(self):            # method
        self.speed = 0

car1 = Car("red", 120)
car1.accelerate()
print(car1.speed)   # 130

Real example: an online shop can have objects such as Product, Order and Customer.

Why it matters: OOP organises big, complex systems. It makes code reusable, easier to maintain and flexible, and lets programmers think about problems in a natural way.

Other paradigms

  • Procedural programming: code is a sequence of functions run from top to bottom, like following a recipe.
  • Functional programming: the program is built from mathematical-style functions that avoid changing data, like equations.

Different paradigms suit different problems. Knowing them helps you pick the right one for each project.

Comparisons

StyleIn simple wordsExamplePros and limits
ProceduralA straight list of steps, split into small functions. Focus: what to do.A recipe: take flour, add water, knead.Pro: simple for small projects. Limit: harder to reuse in big systems; shared state can cause bugs.
Object-orientedModels real things. Each object has its own data and actions. Focus: who does what.A car with a colour and speed that can drive and stop.Pro: great for big systems, reuse and maintenance. Limit: harder for beginners; needs more planning.
FunctionalBuilt on functions that avoid changing state and just turn input into output.A function that doubles a number: same input, same output.Pro: predictable and easy to test. Limit: can feel hard where a lot of state is needed.

The three styles at a glance

Procedural :  step1() -> step2() -> step3()        # what to do
OOP        :  car.accelerate()                    # who does what
Functional :  f(x) = x * 2  ->  f(3) is always 6  # same input, same output
ConditionalsLoops
JobDecide which path to takeDo the same job again and again
Question"Is this condition true?""How many times, or until when?"
Use whenYou need different results in different situationsYou want to automate repeated work
ExamplePass if the score is 60 or more, otherwise failPrint the numbers 0 to 4
VariableFile
Lives inThe computer's memoryThe disk
LastsGone when the program closesStays after the program closes
Used forWorking things out while the program runsDatabases, logs and settings

Two code templates to practise with

A unit test template

Purpose: a basic structure for checking that functions give the right answer for different inputs.

Python — unittest

import unittest

# The functions to be tested
def add(a, b):
    """Adds two numbers."""
    return a + b

def subtract(a, b):
    """Subtracts two numbers."""
    return a - b

# Test cases
class TestMathFunctions(unittest.TestCase):

    def test_add_positive_numbers(self):
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(10, 20), 30)

    def test_add_negative_numbers(self):
        self.assertEqual(add(-1, -1), -2)
        self.assertEqual(add(-5, 3), -2)

    def test_add_zero(self):
        self.assertEqual(add(0, 5), 5)
        self.assertEqual(add(0, 0), 0)

    def test_subtract_positive_numbers(self):
        self.assertEqual(subtract(5, 3), 2)
        self.assertEqual(subtract(10, 5), 5)

    def test_subtract_negative_numbers(self):
        self.assertEqual(subtract(-5, -3), -2)
        self.assertEqual(subtract(3, 5), -2)

# Run the tests
if __name__ == "__main__":
    unittest.main()

What you should see: the test framework runs every test. If they all pass, you get a success message. If one fails, it tells you which test failed and why, so you can fix the code.

Checking an email address with regex

Purpose: use a regular expression to check that an email address typed by a user has the right shape.

Python — re

import re

# A common pattern: username, '@', domain name,
# and a top-level domain (like .com)
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"

def validate_email(email_address):
    """Validates the given email address using a regular expression."""
    if re.match(email_pattern, email_address):
        return True
    return False

test_emails = [
    "test@example.com",
    "john.doe123@sub.domain.co.uk",
    "invalid-email",
    "user@.com",
    "another@example",
    "user@localhost",
]

print("Email validation results:")
print("-" * 20)
for email in test_emails:
    print(f"'{email}' is valid: {validate_email(email)}")

What you should see: each address is checked against the pattern and shows True or False. Read the pattern like this: a user name, then @, then a domain name, then a dot, then at least two letters such as com. That is why another@example fails: it has no final dot part.

Step-by-step checklist

Use this to apply the ideas and check your own code. It shows the whole process of building a small program.

  • Define and analyse the problem: Do I clearly understand what my program must solve? Have I broken it into small parts? Do I know the result I want?
  • Structure and design: Have I picked the functions I need? Do my variables have clear names and the right data type? Should I use classes and objects?
  • Core logic: Are my conditionals correct? Are my loops efficient, and can they end?
  • Error handling: Have I found what could go wrong, such as bad input or a missing file? Do I use try/except? Does the user get a clear message?
  • Reuse: Have I used a suitable library instead of rewriting it? Are imported libraries used correctly?
  • Saving data: If data must survive after closing, have I used file I/O? Do I close files properly, or use with?
  • Validation: Have I checked user input with regex or another method? Does the program handle unexpected data?
  • Testing: Does every important function have unit tests? Do they cover correct, wrong and out-of-range cases? Do they all pass?
  • Review: Is the code clean and commented? Have I removed repeated code? Is it fast enough, if that matters?

Important warnings

1. Memorising syntax is not enough. Coding without understanding the problem leads to software that does not work, wasted time and technical debt.
2. In a while loop, make sure the condition will become false at some point, or the program will be stuck forever.
3. Always close a file you open, or resources are wasted and data can be damaged.
4. If you do not handle errors such as dividing by zero, bad input or a missing file, the program will crash.
5. Run your tests after every change, so something that used to work does not break without you noticing.

Common beginner mistakes

MistakeWhat it looks likeDo this instead
Bad variable namesShort, unclear names such as x, data, tempNames that explain the purpose: user_name, is_active_user, total_price
Logic errors in conditionsWrong order or operator, such as = instead of ==Think through every case and test simple cases first
Infinite loopsA while condition that never becomes falseMake sure something inside the loop changes the controlling variable
Not handling exceptionsNo try-except for division by zero, missing files or bad inputFind the risky places, handle them and tell the user what went wrong
Reinventing the wheelWriting functions that already exist in standard librariesCheck Python's standard library first; it saves time and has fewer bugs
No testsCode without unit tests, so bugs appear unexpectedlyWrite tests with your code, or even first (test-driven development)
Not closing filesForgetting close() after open()Use with open(...) as file:, which closes the file for you
Unclear codeFew or unhelpful commentsWrite code that explains itself; comment why, not just what; add docstrings

Glossary

TermSimple meaning
ProgrammingGiving a computer instructions to do a job.
PythonA popular, high-level, general-purpose language that is easy to read.
FunctionA named block of code that does one job and can be used again and again.
VariableA named place in memory that holds data for a while.
ConditionalThe part of a program that makes logical decisions.
LoopA block of code that repeats until a condition is met.
ExceptionAn unexpected error or unusual situation while a program runs.
LibraryA collection of ready-made code that can be reused.
Unit testA check that the smallest part of a program works correctly.
File I/OReading data from files on the disk and writing data to them.
Regular expression (regex)A powerful language for finding and matching patterns in text.
Object-oriented programming (OOP)A style that models real-world things as objects in code.
Procedural programmingA style where the program is a sequence of functions.
Functional programmingA style based on mathematical functions that avoid changing state.
DebuggingFinding and fixing mistakes in software.
ParadigmA basic style or philosophy for organising code.
SyntaxThe rules for writing code in a language.
AlgorithmA clear, step-by-step set of instructions to solve a problem.
Defensive programmingWriting code that stays stable with unexpected input and situations.
Technical debtThe cost you pay later for rushed, unplanned code.

Practice project: a personal task manager

This homework puts the ideas into practice. Build a simple task manager that runs in the command line. Users can add tasks, see them and mark them as done, and the tasks are saved in a file so they are still there next time.

A terminal window showing the Task Manager menu and two tasks, one pending and one done
When it is finished, your program will show a menu like this.
1

Main structure

Write a main function that runs the program and shows a welcome message.

2

Add a task

Write add_task(task_description). Each task has a description and a status, such as "Pending" or "Done". Ask the user to type the description.

3

View tasks

Write view_tasks() to list every task with a number and its status, for example 1. Drink water [Pending]. If there are no tasks, show "No tasks found".

4

Complete a task

Write complete_task(task_number) to mark a task as done. Use conditionals to check the number is valid, and show a clear error if it is not.

5

Save and load

Write save_tasks() and load_tasks() to store tasks in tasks.txt. Load old tasks when the program starts and save before it closes. Use try-except for file errors.

6

Menu and loop

Show the menu inside a while loop: 1. Add task, 2. View tasks, 3. Complete task, 4. Exit. Run the matching function, and handle wrong input such as letters instead of numbers.

7

Regex (optional)

For an extra challenge, use regex to check a pattern in the task, such as a date in the form YYYY-MM-DD.

8

Unit tests

Write at least three unit tests for each main function (add_task, view_tasks, complete_task) with the unittest module.

The result: a menu where users type a number to add, view and complete tasks, then exit. When they run it again, their tasks load automatically.

The golden lesson: programming in Python is not just writing code. It is solving problems logically and building the skill to face real-world challenges, where every small part becomes part of a bigger solution and the best code is always reliable and usable.

The final formula

Clear understanding of the problem + logical splitting (functions and variables) + decisions and repetition (conditionals and loops) + good error handling (exceptions) + reuse (libraries) + constant checking (unit tests) + lasting data (file I/O) + correct input (regex) + modelling the real world (OOP) = strong, reliable, efficient software.

Quick recall: test yourself

Think of your own answer first, then open the question to check.

What are functions and variables for?+
Functions split code into small, reusable parts that each do one job. Variables store data in memory under a name, so it can be used and changed later.
What can go wrong without exception handling? Give two examples.+
The program can crash, data can be lost and the user is left with an error they do not understand. Examples: dividing by zero, or opening a file that does not exist.
What is the difference between conditionals and loops?+
Conditionals decide which code runs, based on whether a condition is true or false. Loops run the same code again and again.
How do libraries help you avoid "reinventing the wheel"?+
They give you code that is already written and tested, so you do not rewrite common tasks and can focus on your own problem.
What is the main idea of a unit test, and how does it make code reliable?+
A unit test checks one small part of the code on its own, automatically. It finds bugs early, stops old features breaking after a change, documents what the code should do and makes improving the code safer.
Why do we need file input/output when we have variables?+
Variables live in memory and disappear when the program closes. Files are saved on disk, so the data is still there next time.
Give two uses of regular expressions.+
Checking that data has the right shape, such as an email address, and pulling information out of text, such as dates or phone numbers from a big file.
What is the main difference between object-oriented and procedural programming?+
Procedural programming is a list of steps in order: what to do. Object-oriented programming is about objects that have their own data and actions: who does what, like a car with a colour and speed that can accelerate and brake.
How would you make your code defensive? Name two ways besides exception handling.+
Validate user input, for example with regex, and write unit tests that cover correct, wrong and out-of-range cases.
What does "programming is not just writing code" mean?+
The real skill is understanding the problem, breaking it into small parts and solving it logically. Code is only how you write that thinking down.
What is the main goal of this course, and who is it for?+
To teach the basic syntax of Python together with the thinking and good habits behind programming, so learners can solve problems on their own. It is for beginners, including people with no programming experience.
Name at least five main stages of the problem-to-software flow.+
Understand the problem, define the requirements, design and structure, build the logic, handle errors, test, and deliver.

Get new guides by email

Simple, practical guides on websites, AI tools and Python, explained in plain English. About two emails a month. Unsubscribe any time.

We only use your email to send new guides and notes. See our privacy policy.

Syed Rizwan Ahmed
Syed Rizwan Ahmed

Founder, Rizwan's AI Agency. Currently learning Python and sharing his class notes, part by part, for other beginners.

Learning to code too?

More parts are coming over the next few weeks. Read the other guides for students, or ask a question on WhatsApp.

See all guides →