COMPLETE FREE PYTHON COURSE FOR BEGINNERS




Gabbywall Learn

Complete Python Tutorial

Learn Python from the fundamentals to practical programming through clear explanations, examples, exercises and projects.

Introduction

Python is a powerful and beginner-friendly programming language used to build software, automate tasks, analyze data, develop websites, create artificial intelligence systems and much more.

This course is designed to take you from the fundamentals of Python to writing useful programs of your own. You do not need previous programming experience to begin.

You can learn at your own pace, practice each concept and return to any section whenever you need a refresher.

Why Was Python Named Python?

Before learning how to write Python code, it is interesting to know where the name came from.

Python was created by Guido van Rossum and first released in 1991. The name was not chosen because the programming language was related to snakes.

Guido was a fan of a British comedy television series called Monty Python's Flying Circus. While developing the language, he wanted a name that was short, distinctive and slightly playful.

He therefore chose the name Python, inspired by Monty Python.

The snake became strongly associated with Python later, which is why you often see snake imagery and names such as Python represented by a snake.

↑ Back to Python Contents

Learn Python Your Way

People learn differently. Some people understand programming concepts more easily by watching someone explain them, while others prefer reading explanations and working through examples at their own pace.

That is why this course gives you both options. You can watch the embedded video or work through the written lessons below, section by section.

🎥 Learn With Video

Watch the video explanation if you prefer visual demonstrations and spoken explanations.

📖 Learn With Reading

Read through the lessons, study the examples and practice the code at your own pace.

Python Video Lesson

Prefer learning through video? Start here. You can also continue with the written lessons below.

Python Course Contents

Select any topic to open its lessons.

15 Errors & Exceptions
19 Iterators & Generators

Ready to Take Your Projects Online?

As you progress through Python, you may eventually want to publish a website, portfolio or web application online. A reliable hosting service can help you take your projects from your computer to the web.

Recommended: Hostinger

Explore Hostinger → Disclosure: This section contains an affiliate link. Gabbywall may earn a commission if you make a qualifying purchase through the link.
Python — Section 01

Getting Started With Python

Before writing larger Python programs, you need to understand how Python gets onto your computer, where you write your code, how Python runs that code and some of the basic rules that determine whether your program works.

This section takes you from having Python installed to writing and running your first simple Python program.

What You Will Learn

  • How to install Python.
  • How to check whether Python is installed.
  • How Python code is executed.
  • What the Python interpreter does.
  • Different ways to write and run Python code.
  • What an IDE and code editor are.
  • How to create your first Python program.
  • Basic Python syntax.
  • How indentation works in Python.
  • How to write comments.
  • How to avoid some common beginner mistakes.

Installing Python

Python is a programming language, but before you can normally write and execute Python programs on your computer, you need a Python interpreter.

The interpreter is the program that reads your Python source code and executes it. Python's official documentation describes the interpreter as the program through which Python commands and scripts are executed. :contentReference[oaicite:1]{index=1}

Where Do You Get Python?

Python is available for major operating systems including Windows, macOS and Linux. The official Python website provides installers and documentation for supported platforms.

When installing Python, make sure you are installing a current supported version of Python 3.

How Do You Know If Python Is Installed?

After installation, you can check from a terminal or command prompt.

On many systems, you can try:

python --version

Depending on your operating system and installation, you may instead use:

python3 --version

On Windows systems with the Python launcher, you may also see commands using py. The exact command available can depend on how Python was installed. :contentReference[oaicite:2]{index=2}

Example:

If Python is installed correctly, the terminal should display a Python version number.

Python 3.x.x

What If the Command Does Not Work?

If your computer says that the command cannot be found, Python may not be installed, or the command may not be available through your system's PATH configuration.

Do not panic. This does not mean that Python itself is broken. It usually means your computer cannot locate the Python installation using the command you entered.

This is one reason beginners should learn the difference between installing a program and making the program accessible from the terminal.

↑ Back to Python Contents

Running Python

Once Python is installed, there are several ways you can run Python code.

Two important approaches are:

  1. Using Python interactively.
  2. Running a Python script saved in a file.

1. Using Python Interactively

Interactive mode allows you to type Python instructions directly into the interpreter and immediately see the result.

When the interpreter is running interactively, you will commonly see the prompt:

>>>

For example, you can type:

2 + 3

Python immediately evaluates the expression.

Output
5

This makes interactive mode useful when you want to quickly test an idea, experiment with a function or check what a particular Python expression does.

The official Python documentation describes interactive mode as a mode in which the interpreter reads commands and executes them as you enter them. :contentReference[oaicite:3]{index=3}

2. Running a Python Script

For larger programs, you normally write your code in a file and save it with the .py extension.

For example:

hello.py

Inside that file you could write:

print("Hello, Python!")

You can then run the file through your Python installation.

A common command is:

python hello.py

Depending on your operating system and Python setup, the command may instead be:

python3 hello.py

The important idea is simple:

Remember

Interactive mode is excellent for quick experiments. Python files are better for programs that you want to save, reuse, edit and share.

Interactive Python vs Python Files

Interactive Mode Python File
Good for quick experiments Good for complete programs
Results appear immediately Code can be saved
Useful for learning Useful for projects
Usually temporary Can be reused and shared
↑ Back to Python Contents

Python IDEs and Code Editors

You do not have to write Python programs directly inside a terminal. Most programmers use a code editor or an Integrated Development Environment, commonly called an IDE.

What Is a Code Editor?

A code editor is a program designed for writing and editing source code.

A good code editor can make programming easier by providing features such as:

  • Syntax highlighting.
  • Code completion.
  • File management.
  • Search and replace.
  • Error highlighting.
  • Extensions and plugins.

What Is an IDE?

An IDE combines several programming tools into one development environment.

Depending on the IDE, this may include a code editor, debugger, project management tools, terminal integration and other development features.

Examples of Python Development Tools

  • Python IDLE
  • Visual Studio Code
  • PyCharm
  • JupyterLab
  • Other Python-compatible editors and IDEs

You do not need the most complicated development environment to learn Python.

In fact, beginners should focus more on understanding Python than spending too much time changing editors.

Beginner advice:

Pick one development environment, learn how to create and run a Python file, and start writing programs. You can explore more advanced tools as your projects become more demanding.

↑ Back to Python Contents

Your First Python Program

You are now ready to write your first Python program.

One of the simplest things a Python program can do is display information on the screen.

Python provides the print() function for this.

Your First Line of Python

print("Hello, Python!")

What Does This Code Mean?

Let's break it down.

  • print is the name of the function.
  • ( ) tells Python that we are calling the function.
  • "Hello, Python!" is a string of text.
Output
Hello, Python!

Try Changing the Message

Programming becomes easier when you experiment. Change the text inside the quotation marks.

print("I am learning Python.")
Output
I am learning Python.

Try another one:

print("Welcome to Gabbywall Learn!")

This may look extremely simple, and it is. But the important thing is that you have just written a real Python instruction.

Printing More Than One Thing

You can use multiple values inside print().

print("My name is Olivia")
print("I am learning Python")
print("Python is interesting")
Output
My name is Olivia
I am learning Python
Python is interesting
Don't rush past the basics.

A beginner might look at print() and think, "This is too easy." But programming is built from simple instructions combined together. Understanding these small building blocks is what eventually allows you to create large applications.

↑ Back to Python Contents

Python Syntax

Syntax refers to the rules that determine how Python code should be written.

Just as human languages have grammar rules, programming languages have syntax rules.

If you write Python in a way that does not follow its syntax, Python may produce an error instead of executing the program.

Python Is Sensitive to Structure

Python uses indentation as an important part of its syntax. This becomes especially important when you begin learning conditions, loops and functions.

For example:

if 10 > 5:
    print("10 is greater than 5")

The indented line belongs to the if statement.

The colon after the condition indicates that a block of code follows, and the indentation identifies the statements belonging to that block.

Indentation Matters

Python uses indentation to organize blocks of code. This is one of the characteristics that makes Python code visually clean, but it also means indentation mistakes can cause errors or change how your program behaves.

A common convention is to use four spaces for each indentation level.

if age >= 18:
    print("You are an adult")

Python Is Case-Sensitive

Python distinguishes between uppercase and lowercase letters.

For example:

name = "John"
Name = "Mary"

name and Name are different names in Python.

Remember

Python is case-sensitive. Pay attention to uppercase and lowercase letters when writing variables, functions, keywords and other identifiers.

Python Uses Colons in Certain Structures

You will frequently see a colon at the end of statements that introduce a block of code.

For example:

if score >= 50:
    print("Pass")

Later, you will see this pattern with conditions, loops, functions, classes and other Python structures.

Python Uses Parentheses for Function Calls

When calling a function, parentheses are normally used.

print("Hello")

The parentheses contain the information being passed to the function.

Strings Need Quotation Marks

When you want Python to treat something as text, you normally place the text inside quotation marks.

print("Hello")

Single and double quotation marks can both be used for ordinary strings.

print("Hello")
print('Hello')

Both represent text strings.

Whitespace

Python generally allows spaces around operators, and readable spacing is encouraged.

total = 10 + 20

Writing readable code is an important programming habit. Your code should not only work; another person should be able to understand it.

↑ Back to Python Contents

Python Comments

Comments are notes written inside your source code for humans. Python does not execute ordinary comments as program instructions.

Comments are useful for explaining what a section of code does, leaving reminders and making programs easier to understand.

Creating a Comment

A Python comment begins with the hash symbol:

# This is a comment

Python ignores the comment when executing the program. The official Python documentation also describes comments as beginning with the # character and extending to the end of the line. :contentReference[oaicite:4]{index=4}

Commenting Above Your Code

You can place a comment above a line to explain what the code does.

# Display a welcome message
print("Welcome to Python")

Commenting Beside Your Code

You can also place a comment after a line of code.

age = 27  # Store the user's age

Why Are Comments Useful?

Imagine you write a program today and return to it six months later.

You might understand what you were doing today, but after months of working on other projects, some parts of the program may no longer be obvious.

Good comments can help you understand your own decisions later.

Comments can also help other developers understand unfamiliar code.

Comments Should Add Meaning

You do not need to comment every single line of code.

For example, this comment does not add much value:

age = 27  # Set age to 27

The code is already obvious.

A useful comment explains something that may not be immediately obvious.

# Use the user's birth year to estimate their current age
age = current_year - birth_year

Commenting Out Code

During development, programmers sometimes temporarily disable a line of code by turning it into a comment.

# print("This line is temporarily disabled")
print("This line will run")

This can be useful while testing a program.

Important:

Comments are primarily for humans. They should help explain the code rather than become a substitute for writing clear code.

↑ Back to Python Contents

Common Beginner Mistakes

Making mistakes is a normal part of learning programming. The goal is not to avoid every error. The goal is to learn how to understand and fix errors.

1. Forgetting Quotation Marks

If you want to print text, make sure the text is represented as a string.

print("Hello")

A beginner might accidentally write:

print(Hello)

Python does not interpret Hello as ordinary text in this situation.

2. Mixing Up Uppercase and Lowercase

Remember that Python is case-sensitive.

name = "Olivia"

print(name)

This is different from:

name = "Olivia"

print(Name)

3. Incorrect Indentation

When working with blocks of code, make sure the indentation is consistent.

if age >= 18:
    print("Adult")

4. Thinking Errors Mean You Are Bad at Programming

This is perhaps the most damaging beginner mistake.

Programming involves problem solving. Even experienced developers encounter errors, unexpected behavior and bugs.

When Python gives you an error, treat it as information: something about your program needs attention.

Learning to read error messages will become one of your most valuable programming skills.

↑ Back to Python Contents

Practice: Getting Started With Python

Don't just read this section. Open your Python environment and try these exercises yourself.

  1. Write a program that prints your name.
  2. Write a program that prints three things about yourself.
  3. Print the sentence: I am learning Python.
  4. Create a comment explaining what your program does.
  5. Write a program that prints a simple welcome message for someone visiting Gabbywall.
  6. Experiment with both single and double quotation marks.
  7. Open Python's interactive interpreter and calculate 25 + 17.
Challenge

Create a Python program that prints your name, your favourite subject and one reason you are learning Python.

↑ Back to Python Contents

Quick Quiz: Getting Started

1. What is the Python interpreter used for?

2. Which symbol starts a Python comment?

3. Which function can be used to display information?

4. Is Python case-sensitive?

5. What is the usual file extension for Python programs?

↑ Back to Python Contents

Getting Started: Summary

You have now learned the basic setup and concepts needed to begin working with Python.

Concept What It Means
Python Interpreter The program that executes Python code.
Interactive Mode A way to enter Python commands and see results immediately.
Python Script A Python program saved in a file, normally with a .py extension.
IDE A development environment containing tools that help you write and work with code.
Syntax The rules governing how Python code is written.
Indentation Whitespace used to organize blocks of Python code.
Comment A note in source code that Python does not execute as ordinary program instructions.
print() A built-in function commonly used to display information.
You are ready for the next step.

At this point, you know how Python is installed, how Python code can be executed, how to create a basic Python program, and some of the fundamental syntax rules.

Next, we begin one of the most important concepts in programming: variables.

↑ Back to Python Contents

Next: Python Variables

Learn how Python stores and works with information using variables.

Continue to Python Variables →
02

Python Variables

Variables are one of the most important ideas you will learn in Python. Almost every useful Python program will need them.

If you have never programmed before, the word variable may sound complicated. It is actually a very simple idea.

In this lesson, we are going to take our time and understand variables from the ground up. By the end, you should be able to create variables, store information in them, change their values, use them in calculations, accept information from a user, and choose good variable names.

What You Will Learn

  • What a variable is
  • Why programmers use variables
  • How to create a variable
  • How assignment works in Python
  • How to change a variable's value
  • How to print variables
  • How to use variables in calculations
  • How to store different kinds of values
  • How to name variables correctly
  • How to use multiple variables
  • How variables work with user input
  • The difference between local and global variables
  • Common mistakes beginners make with variables

What Is a Variable?

A variable is a name that Python uses to refer to a value.

That definition may sound technical, so let's forget the technical language for a moment.

Imagine that you have several boxes in your room. You put a label on each box so that you know what is inside.

Think About It Like This

You could have a box labelled:

  • Name
  • Age
  • Country

Inside those boxes you could have:

  • Name → Olivia
  • Age → 27
  • Country → Nigeria

Programming variables work in a similar way. The variable name gives us a convenient way to refer to information stored by our program.

In Python, we could write:

name = "Olivia"
age = 27
country = "Nigeria"

Here we have created three variables:

  • name
  • age
  • country

Each variable refers to a different value.

Why Do We Use Variables?

You might be wondering:

"Why can't I just write the values directly?"

Sometimes you can. But real programs usually need to work with information that changes.

Imagine you are creating a program that calculates the total cost of fuel for a trip.

You might have:

fuel_price = 950
litres = 48.93

You can then calculate the total:

total_cost = fuel_price * litres

print(total_cost)

Instead of repeatedly writing 950 and 48.93, we give those values meaningful names.

This makes the program easier to understand and easier to change later.

Without Variables

print(950 * 48.93)

This works, but another programmer looking at it has to figure out what 950 and 48.93 represent.

With Variables

fuel_price = 950
litres = 48.93

total_cost = fuel_price * litres

print(total_cost)

Now the code explains itself much better.

Creating Variables

Creating a variable in Python is very simple.

You give the variable a name, type the equals sign =, and then provide the value.

name = "Olivia"

You can read this informally as:

"Store the value Olivia under the name name."

You can create a number variable:

age = 27

You can create a decimal number:

price = 1500.50

And you can store a Boolean value:

is_student = True

We will study these different types of values in much more detail in the next major section, Python Data Types.

Assigning Values to Variables

One of the most important things to understand about Python variables is the meaning of the equals sign =.

In mathematics, you might see:

x = 10

In programming, we normally describe this as assignment.

The value on the right is assigned to the name on the left.

1
Python sees the value

Python evaluates the right side: 10.

2
Python uses the name

Python associates that value with x.

3
You can use the variable later

You can now use x elsewhere in your program.

x = 10

print(x)
Output
10

Python's documentation describes assignment as binding a name to a value. :contentReference[oaicite:1]{index=1}

Important: = Does Not Mean "Is Equal To"

At this stage, think of = as "assign this value".

Python uses == when we want to compare whether two things are equal. We will learn that later in the Operators and Conditions sections.

Printing Variables

Once you create a variable, you can display its value using the print() function.

name = "Olivia"

print(name)
Output
Olivia

You can also print several variables:

name = "Olivia"
age = 27
country = "Nigeria"

print(name)
print(age)
print(country)
Output
Olivia
27
Nigeria

Notice something important here: print(name) does not print the word name. It prints the value stored under that variable.

Changing the Value of a Variable

Variables are called variables because the value associated with a name can change during a program.

For example:

age = 27

print(age)

age = 28

print(age)
Output
27
28

The first time we assign a value:

age = 27

age refers to 27.

Later, we assign another value:

age = 28

Now age refers to 28.

Think of It Like Updating a Label

Imagine a box labelled age. At first, the information inside says 27.

Later, you update the information to 28.

You are not creating a completely different concept. You are updating what your program associates with that name.

Variables Can Store Different Kinds of Values

Python variables can refer to many different kinds of values.

For example:

name = "Olivia"
age = 27
height = 1.68
is_student = True

Here:

Variable Value Example Type
name "Olivia" String
age 27 Integer
height 1.68 Float
is_student True Boolean

We will explore all of these properly in the Data Types section.

Using Variables in Calculations

One of the biggest reasons variables are useful is that they allow us to perform calculations using meaningful names.

For example, imagine a simple shopping calculation.

price = 5000
quantity = 3

total = price * quantity

print(total)
Output
15000

Python calculates:

5000 * 3

and stores the answer in:

total

This becomes especially powerful when the values come from users, files, databases, sensors, or other parts of a program.

Python Variable Names

Choosing a variable name may seem like a small thing, but it is extremely important.

Compare these two programs:

x = 950
y = 48.93
z = x * y

with:

fuel_price = 950
litres = 48.93
total_cost = fuel_price * litres

Both can perform the calculation, but the second version tells us what each value represents.

Good variable names make your code easier to read, understand, debug, and maintain.

Rules for Naming Variables

Python has rules that variable names must follow.

Rule 1: A variable name can contain letters

name = "Olivia"
country = "Nigeria"

Rule 2: A variable name can contain numbers

student1 = "Ada"
student2 = "David"

However, a variable name cannot begin with a number.

1student = "Ada"

The example above is invalid.

Rule 3: Underscores are allowed

first_name = "Olivia"
last_name = "Ezeani"
fuel_price = 950

Rule 4: Spaces are not allowed

This is invalid:

first name = "Olivia"

Use an underscore instead:

first_name = "Olivia"

Rule 5: Variable names are case-sensitive

This means that Python treats these as different names:

name = "Olivia"
Name = "David"
NAME = "Sarah"

They are three different variable names.

Be Careful With Capital Letters

A common beginner mistake is creating:

name = "Olivia"

print(Name)

Python will not assume that Name means name.

Good and Bad Variable Names

Python allows many names that technically work, but "working" does not always mean "good programming".

Variable Good? Why?
name Good Simple and clear
student_name Good Clearly describes the value
fuel_price Good Describes exactly what it stores
x Sometimes Okay for short mathematical examples
a_really_long_name_that_is_hard_to_read Not ideal Too long
thing Not ideal Does not explain what it contains

Use Descriptive Names

Instead of:

x = 350000

you could write:

salary = 350000

Now anyone reading the code immediately has an idea what 350000 represents.

Python Naming Style: snake_case

Python programmers commonly use a style called snake_case for variable names.

Instead of writing:

studentname = "Olivia"

you can write:

student_name = "Olivia"

For multiple words:

first_name = "Olivia"
last_name = "Ezeani"
phone_number = "08000000000"
total_trip_cost = 45000

The underscore makes the words easier to read.

Assigning Values

We have already seen simple assignment:

name = "Olivia"

But the right side of the equals sign can also contain an expression.

price = 5000
quantity = 4

total = price * quantity

Python first works out:

5000 * 4

and then assigns the result to total.

print(total)
Output
20000

Reassigning Variables

You can assign a new value to an existing variable.

score = 50

print(score)

score = 80

print(score)
Output
50
80

The variable did not become a completely new variable. We simply assigned a new value to the existing name.

Python's documentation describes this as rebinding a name to a value. :contentReference[oaicite:2]{index=2}

Updating a Variable's Value

Suppose you have:

score = 10

score = score + 5

print(score)
Output
15

Python also provides a shorter way to write this:

score = 10

score += 5

print(score)
Output
15

You will see operators such as +=, -=, and *= frequently in real Python programs. Python's reference documentation defines these as augmented assignment operations. :contentReference[oaicite:3]{index=3}

For now, simply remember:

  • x += 5 means increase x by 5.
  • x -= 5 means decrease x by 5.
  • x *= 5 means multiply x by 5.

Multiple Variables

You can create several variables one after another:

name = "Olivia"
age = 27
country = "Nigeria"

Python also allows multiple assignment in a single line.

x, y, z = 10, 20, 30

print(x)
print(y)
print(z)
Output
10
20
30

The values are matched from left to right:

x = 10
y = 20
z = 30

The shorter version is:

x, y, z = 10, 20, 30

Python supports this type of multiple assignment directly. :contentReference[oaicite:4]{index=4}

Giving Multiple Variables the Same Value

You can also give several variables the same value.

x = y = z = 0

Now:

print(x)
print(y)
print(z)
Output
0
0
0

This can be useful when several values need the same starting value.

Variables and User Input

Variables become even more useful when your program receives information from a user.

Remember the input() function from earlier? We can store the user's response in a variable.

name = input("What is your name? ")

print(name)

When the program runs, the user might type:

Olivia

The response is stored in the variable:

name

You can then use that variable later:

name = input("What is your name? ")

print("Hello", name)

If the user enters Olivia, the program displays:

Hello Olivia

A Very Important Point

By default, input() gives your program the user's response as text.

We will learn how to convert that text into numbers using int() and float() in the Data Types section.

Checking the Type of a Variable

Sometimes you may want to know what kind of value a variable contains.

Python provides the type() function for this.

name = "Olivia"

print(type(name))

You might see:

<class 'str'>

For a number:

age = 27

print(type(age))
<class 'int'>

Do not worry if str and int look unfamiliar right now. We will study data types properly in the next section.

Local and Global Variables

Now we are going to introduce an idea that may feel a little more advanced.

It is called variable scope.

Scope basically means:

"Where can this variable be accessed?"

There are two terms you will hear often:

  • Local variable
  • Global variable

Global Variable

A variable created outside a function can be accessible from code within that module, including from functions under normal name-resolution rules.

name = "Olivia"

def say_hello():
    print(name)

say_hello()
Olivia

Here, name was created outside the function.

Local Variable

A variable created inside a function is normally local to that function.

def greet():
    message = "Hello"
    print(message)

greet()

The variable message belongs to the function's local scope.

Beginner Rule

Do not worry about using the global keyword yet.

For beginner programs, it is usually better to pass information into functions and return results rather than relying heavily on global variables.

The global Keyword

Python also provides the global keyword. It allows a function to indicate that an assignment should refer to a global name rather than creating a new local binding.

For example:

score = 10

def increase_score():
    global score
    score = score + 5

increase_score()

print(score)
15

This is useful to understand, but you do not need to start writing programs this way immediately.

Learn it now. Use it carefully.

As your programs become larger, relying on many global variables can make your code harder to understand.

What About Constants?

Sometimes a program contains a value that we do not intend to change.

For example:

PI = 3.14159

Python does not have a special constant variable mechanism that prevents reassignment in ordinary Python code. Instead, programmers commonly use uppercase names to signal that a value is intended to remain unchanged.

MAX_SCORE = 100
TAX_RATE = 0.075

Think of uppercase names here as a programmer convention.

Common Beginner Mistakes With Variables

Mistake 1: Forgetting quotation marks around text

This is wrong:

name = Olivia

Python will interpret Olivia as another name rather than text.

Write:

name = "Olivia"

Mistake 2: Starting a variable name with a number

2name = "Olivia"

This is invalid.

Instead:

name2 = "Olivia"

Mistake 3: Using spaces

first name = "Olivia"

Use:

first_name = "Olivia"

Mistake 4: Mixing uppercase and lowercase accidentally

name = "Olivia"

print(Name)

Remember: name and Name are different.

Mistake 5: Using a variable before creating it

print(age)

If age has not been defined before this point, Python will raise a NameError. The official Python tutorial demonstrates this behavior when an undefined variable is referenced. :contentReference[oaicite:5]{index=5}

Create the variable first:

age = 27

print(age)

Mistake 6: Confusing = and ==

Assignment:

age = 27

Comparison:

age == 27

We will study comparison operators later.

Real-World Examples of Variables

Let's make variables feel less abstract by connecting them to programs you might actually build.

Example 1: Student Information

student_name = "Olivia"
age = 27
course = "Python"

print(student_name)
print(age)
print(course)

Example 2: Currency Converter

dollars = 100
exchange_rate = 1500

naira = dollars * exchange_rate

print(naira)

Example 3: Trip Cost

distance = 500
fuel_efficiency = 12
fuel_price = 950

litres_needed = distance / fuel_efficiency
fuel_cost = litres_needed * fuel_price

print(fuel_cost)

Notice how much easier this is to understand because the variables describe what the numbers represent.

Example 4: Agricultural Sensor Project

Imagine that one day you build an agricultural monitoring system that receives information from sensors.

temperature = 31.5
soil_moisture = 42
crop_name = "Maize"

print(temperature)
print(soil_moisture)
print(crop_name)

This is one reason variables are so important in programming: programs need names for the information they are working with.

Practice: Create Your First Variables

Do not just read this section. Open your Python editor and type the examples yourself.

Create variables for:

  1. Your name
  2. Your age
  3. Your country
  4. Your favorite programming language
  5. Your favorite number

Then print all of them.

name = "Your Name"
age = 27
country = "Nigeria"
favorite_language = "Python"
favorite_number = 7

print(name)
print(age)
print(country)
print(favorite_language)
print(favorite_number)
Replace the example values with your own values.

Practice: Build a Simple Calculation

Imagine you bought three items.

  • Item price: ₦2,500
  • Quantity: 4

Create variables for the price and quantity. Then calculate the total.

Your program should produce:

10000

Try solving it yourself before looking at the example below.

Show Example Solution
price = 2500
quantity = 4

total = price * quantity

print(total)

Practice: Change a Variable

Create a variable called score.

Give it the value 50.

Print it.

Then change it to 75 and print it again.

Your output should be:

50
75

Mini Project: Personal Introduction

Let's combine what you have learned.

Create variables for:

  • Name
  • Age
  • Country
  • Favorite programming language

Then use print() to display an introduction.

For example, your program could produce something similar to:

My name is Olivia.
I am 27 years old.
I am from Nigeria.
I am learning Python.

Try to build it yourself before looking at a possible solution.

Show Example Solution
name = "Olivia"
age = 27
country = "Nigeria"
language = "Python"

print("My name is", name)
print("I am", age, "years old.")
print("I am from", country)
print("I am learning", language)

Quick Quiz: Python Variables

Test yourself before moving to the next section.

1. What is a variable?

2. What does the = sign normally do in an assignment?

3. Which is a valid variable name?

4. Are Python variable names case-sensitive?

5. What will this print?

age = 27
age = 28

print(age)

6. What does input() allow a program to do?

Python Variables: Summary

You have now learned one of the foundations of Python programming.

Concept Example Meaning
Creating a variable name = "Olivia" Assign a value to a name
Number variable age = 27 Store a number
Changing a value age = 28 Reassign the variable
Printing print(age) Display the value
Calculation total = price * quantity Use variables in expressions
User input name = input() Store user input
Check type type(age) Find the value's type
Multiple assignment x, y = 10, 20 Assign multiple values

If You Remember Only Five Things

  1. A variable is a name used to refer to a value.
  2. Use = to assign a value.
  3. Variable names should be clear and descriptive.
  4. Python variable names are case-sensitive.
  5. Variables allow programs to store, reuse, and change information.
03

Python Data Types

Now that you understand variables, it is time to learn something very important: the kind of information a variable contains.

This is called a data type.

If the word "type" sounds confusing, don't worry. We are going to explain it using simple, everyday examples before looking at the technical definition.

By the end of this lesson, you should understand the difference between text, whole numbers, decimal numbers, Boolean values, complex numbers, and the special None value.

What You Will Learn

  • What a data type is
  • Why data types matter
  • How to identify a value's type
  • Strings
  • Integers
  • Floats
  • Complex numbers
  • Booleans
  • The special None value
  • How Python treats different types
  • Type conversion
  • How to convert strings to numbers
  • Common beginner mistakes with data types

What Is a Data Type?

A data type tells Python what kind of value it is dealing with.

Think about information in the real world.

You can have:

  • A person's name
  • A person's age
  • A temperature
  • Whether someone is logged in
  • A location
  • A phone number

Although all of these are pieces of information, they are not necessarily the same kind of information.

Think About a School

Imagine a teacher has a record containing:

  • Name → Olivia
  • Age → 27
  • Average score → 82.5
  • Passed → True

The information is different in nature.

Olivia is text.

27 is a whole number.

82.5 is a decimal number.

True represents a yes/no or true/false condition.

Python uses different data types to represent these different kinds of values.

Why Do Data Types Matter?

This is an important question.

Why doesn't Python simply treat everything as "information"?

Because Python needs to know what operations make sense for a particular value.

For example, adding two numbers makes sense:

10 + 5

The result is:

Output
15

But text behaves differently.

"Hello" + "World"

Python joins the two strings:

Output
HelloWorld

So the same + symbol can behave differently depending on the types of values involved.

Data types help Python understand what kind of information it is working with.

Common Python Data Types

Python has many built-in types. For beginners, the most important ones to understand first are:

Type Example Used For
str "Hello" Text
int 27 Whole numbers
float 27.5 Decimal numbers
complex 3 + 4j Complex numbers
bool True True/false values
NoneType None Absence of a value

Python's official documentation lists these among its built-in types. :contentReference[oaicite:1]{index=1}

How to Find the Type of a Value

Python provides a built-in function called type().

You can give type() a value, and Python will tell you what type it is.

print(type("Hello"))

The result is:

<class 'str'>

This tells us that "Hello" is a string.

Try a whole number:

print(type(27))
<class 'int'>

Try a decimal:

print(type(27.5))
<class 'float'>

The type() built-in is the normal way to inspect the type of an object in Python. :contentReference[oaicite:2]{index=2}

Strings

A string is text.

If you are storing words, sentences, names, addresses, messages, or other textual information, you will usually use a string.

Python's type name for strings is:

str

Examples:

name = "Olivia"
country = "Nigeria"
message = "Welcome to Python"

Strings are usually written inside quotation marks.

You can use single quotes:

name = 'Olivia'

Or double quotes:

name = "Olivia"

Both create strings.

Why Do We Need Quotation Marks?

Compare:

name = "Olivia"

with:

name = Olivia

In the first example, Python understands Olivia as text.

In the second example, Python assumes Olivia is the name of another variable.

Python strings are sequences of Unicode characters and are immutable, meaning the existing string itself cannot be changed in place. :contentReference[oaicite:3]{index=3}

String Examples

Strings can contain single words:

name = "Olivia"

Multiple words:

full_name = "Olivia Ezeani"

Sentences:

message = "Welcome to Gabbywall."

Numbers can also appear inside a string.

phone = "08012345678"

Notice that the number above is inside quotation marks. Therefore Python treats it as text, not as a number.

"27" and 27 are not the same type.

The first is a string. The second is an integer.

Integers

An integer is a whole number.

Python's type name for integers is:

int

Examples:

age = 27
students = 50
temperature = 30

Integers can also be negative:

temperature = -5

And zero is also an integer:

score = 0

Python integers can represent whole numbers with arbitrary precision, subject to available memory. :contentReference[oaicite:4]{index=4}

Working With Integers

You can perform mathematical operations with integers.

a = 10
b = 5

print(a + b)
print(a - b)
print(a * b)
print(a / b)
Output
15
5
50
2.0

Notice something interesting:

Even though both a and b are integers, regular division with / produces a floating-point result.

Floats

A float is a floating-point number.

For a beginner, the easiest way to think about a float is:

A number that can contain a decimal part.

Examples:

price = 1500.50
temperature = 31.5
distance = 48.93

You can check:

print(type(31.5))
<class 'float'>

Working With Floats

Floats are especially useful when working with measurements, prices, percentages, distances, temperatures, and other values that may contain decimals.

distance = 48.93
fuel_price = 950

cost = distance * fuel_price

print(cost)
46483.5

The result is also a floating-point number because it contains a decimal part.

Integer vs Float

This distinction is important.

Value Type
10 int
10.0 float
-5 int
-5.5 float
0 int
0.0 float

Notice that 10 and 10.0 represent numerically equivalent values, but they have different Python types.

Complex Numbers

Python also supports complex numbers.

If you have not studied complex numbers in mathematics yet, do not panic.

You do not need to master them to continue learning basic Python.

A Python complex number can look like:

z = 3 + 4j

Here:

  • 3 is the real part.
  • 4j represents the imaginary part.

You can check its type:

print(type(z))
<class 'complex'>

Python's numeric type system includes integers, floating-point numbers, and complex numbers. :contentReference[oaicite:5]{index=5}

Beginner note:

Complex numbers are important in areas such as engineering, physics, signal processing, and mathematics, but you can continue with this course without going deeply into them right now.

Booleans

A Boolean represents one of two truth values:

  • True
  • False

Python's type name for Boolean values is:

bool

Examples:

is_logged_in = True
is_raining = False
has_permission = True

Think of Boolean values as answers to questions that can be answered with yes or no.

Real-World Example

Imagine an application asking:

  • Is the user logged in?
  • Is the payment complete?
  • Is the vehicle available?
  • Is the soil dry?

Each question could have a Boolean answer: True or False.

Python's bool type has exactly two Boolean values: True and False. :contentReference[oaicite:6]{index=6}

True and False Must Be Capitalized

Python uses:

True
False

The first letter is capitalized.

These are not the same:

True
true

Python recognizes True as the Boolean value.

Lowercase true is not the Python Boolean constant.

Python is case-sensitive.

Always write: True and False.

None

Now we come to a special Python value: None.

This can be confusing at first.

The easiest way to think about None is:

"There is currently no value here."

For example:

result = None

print(result)
None

You might use None when a value does not exist yet or when a program needs to represent the absence of a meaningful value.

Example

middle_name = None

This could mean that the person currently has no middle name recorded in the program.

Python has a single null object called None, whose type is NoneType. :contentReference[oaicite:7]{index=7}

Is None the Same as False?

No.

They are different values and different concepts.

answer = None
completed = False

The first says:

There is no value currently.

The second says:

The answer to a yes/no condition is false.

Python does consider None false in a Boolean context, but that does not make None and False the same value. :contentReference[oaicite:8]{index=8}

Type Conversion

Sometimes you have one type of value but need another type.

This is called type conversion.

For example, imagine a user enters their age:

age = input("Enter your age: ")

The value returned by input() is text.

If the user enters:

27

Python receives that input as a string.

We can convert it into an integer using int().

age = int(input("Enter your age: "))

print(age)

Now age is an integer, assuming the user entered something that can be interpreted as an integer.

Python provides constructors such as int(), float(), and complex() for creating numeric values of those types. :contentReference[oaicite:9]{index=9}

Converting to an Integer

Use int() when you need an integer.

number = int("25")

print(number)
print(type(number))
25
<class 'int'>

Before conversion:

"25"

is a string.

After:

int("25")

the result is an integer.

Converting to a Float

Use float() when you need a floating-point number.

price = float("1500.50")

print(price)
print(type(price))
1500.5
<class 'float'>

This is particularly useful when receiving decimal values from a user.

distance = float(input("Enter distance: "))

Converting to a String

You can use str() to convert a value into text.

age = 27

age_text = str(age)

print(age_text)
print(type(age_text))
27
<class 'str'>

This becomes useful when you need to combine a value with text in situations where explicit conversion is required.

Converting Values to Boolean

Python also has:

bool()

It converts a value into a Boolean truth value.

For example:

print(bool(1))
print(bool(0))
True
False

Python has rules for determining whether different objects are considered true or false. For example, zero and None are considered false. :contentReference[oaicite:10]{index=10}

Don't Memorize Everything Yet

Boolean truth testing becomes much more important when we reach if statements and while loops.

For now, understand the basic idea: bool() asks Python to interpret a value as true or false.

Very Important: "10" Is Not the Same as 10

This is one of the most common things that confuses beginners.

Look at these two values:

"10"
10

They look almost identical.

But they are different types.

print(type("10"))
print(type(10))
<class 'str'>
<class 'int'>

This difference becomes very important with calculations.

For example:

number = "10"

print(number + "5")
105

Why?

Because Python is joining two strings.

But:

number = 10

print(number + 5)
15

Here Python is adding two numbers.

Quotation marks can completely change the type of a value.

Why Input Conversion Is Important

Imagine you want a program to ask someone for two numbers.

A beginner might write:

first = input("Enter first number: ")
second = input("Enter second number: ")

print(first + second)

If the user enters:

10
5

the result may be:

105

That is because the values received from input() are strings.

If you want mathematical addition, convert them:

first = int(input("Enter first number: "))
second = int(input("Enter second number: "))

print(first + second)

Now:

15
This is one of the most important beginner lessons in Python.

User input is text by default, so convert it when your program needs a number.

Important Conversion Functions

Function Purpose Example
int() Convert to integer int("25")
float() Convert to float float("25.5")
str() Convert to string str(25)
bool() Convert to Boolean bool(1)

Common Beginner Mistakes With Data Types

Mistake 1: Treating a string like a number

age = "27"

print(age + 5)

This causes a type error because Python cannot directly add a string and an integer this way.

Convert it first:

age = "27"

age = int(age)

print(age + 5)

Mistake 2: Forgetting quotation marks

name = Olivia

If Olivia is meant to be text, use quotes:

name = "Olivia"

Mistake 3: Assuming 10 and "10" are the same

number1 = 10
number2 = "10"

print(type(number1))
print(type(number2))

They have different types.

Mistake 4: Writing true instead of True

is_active = true

Use:

is_active = True

Mistake 5: Trying to convert invalid text to a number

age = int("hello")

Python cannot interpret "hello" as an integer, so this will raise an error.

Type conversion only works when the original value can be converted appropriately.

Real-World Examples

Let's bring everything together using situations you may actually encounter when building applications.

Example 1: Student Record

student_name = "Olivia"
age = 27
average_score = 82.5
passed = True

Here we have:

  • student_name → string
  • age → integer
  • average_score → float
  • passed → Boolean

Example 2: Trip Planner

destination = "Abuja"
distance = 760.5
fuel_price = 950
trip_confirmed = True

Example 3: Agricultural Monitoring

crop = "Maize"
temperature = 31.7
soil_moisture = 42
sensor_active = True

As you move into more advanced programming, your programs will constantly be working with different types of data.

Practice: Identify the Data Types

Look at each value and identify its type before checking the answers.

"Python"
25
25.5
True
None
3 + 4j
Show Answers
  • "Python"str
  • 25int
  • 25.5float
  • Truebool
  • NoneNoneType
  • 3 + 4jcomplex

Practice: Convert the Values

Convert the following values into the requested types.

  1. Convert "50" into an integer.
  2. Convert "25.5" into a float.
  3. Convert 100 into a string.

Try them yourself first.

Show Example Solution
number = int("50")

price = float("25.5")

text = str(100)

Practice: Build a Number Calculator

Write a program that asks the user for two numbers and adds them together.

Remember: input() gives you text, so you need to convert the input before performing mathematical addition.

Try to write the program yourself.

Show Example Solution
first = int(input("Enter first number: "))
second = int(input("Enter second number: "))

total = first + second

print(total)

Mini Project: Student Score Calculator

Let's use variables and data types together.

Ask the user for three scores:

  • Biology
  • Chemistry
  • Physics

Convert the input into integers and calculate the total.

Your program should contain variables similar to:

biology = int(input("Biology score: "))
chemistry = int(input("Chemistry score: "))
physics = int(input("Physics score: "))

total = biology + chemistry + physics

print("Total:", total)

Don't simply copy this.

First try to build it yourself.

Quick Quiz: Python Data Types

Test your understanding before continuing.

1. What is a data type?

2. What type is "Hello"?

3. What type is 25?

4. What type is 25.5?

5. Which two values are Boolean values?

6. What does None generally represent?

7. What is the type of "27"?

8. Which function converts text into an integer?

Python Data Types: Summary

You have now learned that Python values can belong to different types, and that the type affects how Python handles the value.

Type Example Simple Meaning
str "Olivia" Text
int 27 Whole number
float 27.5 Decimal number
complex 3 + 4j Complex number
bool True True/false value
NoneType None Absence of a value

If You Remember Only Seven Things

  1. A data type describes what kind of value you have.
  2. str is used for text.
  3. int is used for whole numbers.
  4. float is used for floating-point numbers.
  5. bool represents True or False.
  6. None represents the absence of a value.
  7. Functions such as int(), float(), str(), and bool() can be used for type conversion.
04

Python Operators

You have learned about variables and data types. Now it is time to make those values useful.

This is where operators come in.

Operators are symbols or keywords that tell Python to perform an operation.

If that sounds complicated, think about mathematics.

You already know symbols such as:

+
-
*
/

You use them to add, subtract, multiply and divide.

Python uses these same ideas, but it has many more operators that allow your programs to calculate, compare, test and make decisions.

What You Will Learn

  • What operators are
  • Arithmetic operators
  • Assignment operators
  • Comparison operators
  • Logical operators
  • Identity operators
  • Membership operators
  • The difference between = and ==
  • How operators work with variables
  • How to combine operators
  • Operator precedence
  • Common beginner mistakes

What Are Operators?

An operator is something that tells Python to perform an operation on one or more values.

For example:

10 + 5

Here:

  • 10 is a value.
  • 5 is a value.
  • + is the operator.

Python uses the + operator to add the two values together.

Result
15
Think of an operator as an instruction.

The values are what Python works with. The operator tells Python what to do with them.

Types of Operators in Python

Python has several categories of operators.

Category Main Purpose
Arithmetic Perform mathematical calculations
Assignment Assign or update values
Comparison Compare values
Logical Combine or reverse conditions
Identity Check whether two references are the same object
Membership Check whether something exists inside a collection

We will take each category slowly.

Arithmetic Operators

Arithmetic operators are used for mathematical calculations.

These are probably the easiest operators to understand because you have already encountered most of them in mathematics.

Operator Name Example Result
+ Addition 10 + 3 13
- Subtraction 10 - 3 7
* Multiplication 10 * 3 30
/ Division 10 / 3 3.333...
// Floor division 10 // 3 3
% Modulus 10 % 3 1
** Exponentiation 10 ** 2 100

Python's numeric types support operations such as addition, subtraction, multiplication, division, floor division, remainder and exponentiation. :contentReference[oaicite:1]{index=1}

The Addition Operator: +

The + operator adds values together.

first = 10
second = 5

result = first + second

print(result)
15

You can also add more than two values:

total = 10 + 20 + 30

print(total)
60

Adding Strings

The + operator can also join strings.

first_name = "Olivia"
last_name = "Ezeani"

full_name = first_name + " " + last_name

print(full_name)
Olivia Ezeani
The same operator can behave differently depending on the types of values being used.

The Subtraction Operator: -

The - operator subtracts one value from another.

money = 5000
spent = 1500

remaining = money - spent

print(remaining)
3500

This is useful in many real-world programs.

For example:

  • Calculating remaining money
  • Calculating remaining inventory
  • Calculating remaining fuel
  • Calculating age differences
  • Calculating available seats

The Multiplication Operator: *

The * operator multiplies values.

price = 1500
quantity = 4

total = price * quantity

print(total)
6000

This is useful for calculating things such as:

  • Total product cost
  • Distance
  • Area
  • Salary calculations
  • Fuel cost

The Division Operator: /

The / operator performs division.

total = 20
people = 4

result = total / people

print(result)
5.0

Notice that the result is 5.0, not 5.

Regular division with / produces a floating-point result.

Floor Division: //

Floor division uses two forward slashes:

//

It performs division and returns the floor of the result.

result = 10 // 3

print(result)
3

Compare:

print(10 / 3)
print(10 // 3)
3.3333333333333335
3

Simple Way to Remember

/ asks: "What is the exact division result?"

// asks: "What is the floor of the division result?"

The Modulus Operator: %

The % operator gives you the remainder after division.

For example:

result = 10 % 3

print(result)
1

Why?

Because:

10 ÷ 3 = 3 remainder 1

Therefore:

10 % 3 = 1

Why Is This Useful?

The modulus operator is extremely useful when checking whether a number is even or odd.

number = 10

print(number % 2)
0

If a number divided by 2 leaves a remainder of zero, it is even.

For example:

10 % 2
12 % 2
7 % 2
0
0
1

This idea becomes very useful when we learn conditions and loops.

Exponentiation: **

The ** operator is used for powers.

For example:

result = 2 ** 3

print(result)
8

This means:

2 × 2 × 2 = 8

Another example:

print(5 ** 2)
25

Assignment Operators

Assignment operators are used to assign values to variables.

You have already seen:

age = 27

The = symbol means:

"Take the value on the right and assign it to the variable on the left."

It does not mean "is equal to" in the mathematical sense.

Understanding the = Sign

Consider:

age = 27

Read it as:

"Assign 27 to age."

Python evaluates the right-hand side and then assigns the result to the name on the left.

For example:

price = 100
quantity = 3

total = price * quantity

Python calculates:

100 * 3

and assigns the result to total.

Therefore:

print(total)
300

Compound Assignment Operators

Python also allows you to combine an arithmetic operation with assignment.

For example:

score = 10

score += 5

print(score)
15

This:

score += 5

is a shorter way of writing:

score = score + 5

Other examples include:

score -= 2
score *= 3
score /= 2
score //= 2
score %= 2
score **= 2
Operator Meaning
+= Add and assign
-= Subtract and assign
*= Multiply and assign
/= Divide and assign
//= Floor divide and assign
%= Modulus and assign
**= Power and assign

Comparison Operators

Comparison operators are used when you want Python to compare two values.

They produce a Boolean result:

  • True
  • False

Python provides comparison operators such as <, >, ==, !=, <= and >=. :contentReference[oaicite:2]{index=2}

Operator Meaning
== Equal to
!= Not equal to
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to

The Equal-To Operator: ==

The == operator checks whether two values are equal.

age = 27

print(age == 27)
True

But:

age = 27

print(age == 30)
False
Remember:

= means assignment.

== means comparison.

The Not-Equal Operator: !=

The != operator asks:

"Are these values different?"
age = 27

print(age != 30)
True

Because 27 is not 30.

Greater Than and Less Than

You can ask whether one number is larger or smaller than another.

age = 27

print(age > 18)
print(age < 18)
True
False

You can also use:

>=
<=

These include equality.

age = 18

print(age >= 18)
print(age <= 18)
True
True

Logical Operators

Logical operators allow you to combine or reverse conditions.

Python has three main Boolean logical operators:

  • and
  • or
  • not

These become extremely important when we start writing conditions.

The and Operator

and means that multiple conditions must be true for the combined expression to be true.

Imagine:

You can enter an exam if:

  • You registered
  • and you paid the fee

In Python:

registered = True
paid = True

print(registered and paid)
True

If either condition is false:

registered = True
paid = False

print(registered and paid)
False

Python evaluates and expressions from left to right and can stop once the final result is already determined. :contentReference[oaicite:3]{index=3}

The or Operator

or means that at least one of the conditions needs to be true for the combined expression to be true.

Imagine a website accepts payment by:

  • Card
  • or bank transfer
card = False
bank_transfer = True

print(card or bank_transfer)
True

Because at least one condition is true.

The not Operator

not reverses a Boolean truth value.

is_raining = True

print(not is_raining)
False

Because:

not True = False

And:

not False = True

Identity Operators

Identity operators are:

  • is
  • is not

They check whether two references point to the same object.

This is different from simply asking whether two values are equal.

Python's documentation defines is and is not as identity comparisons. :contentReference[oaicite:4]{index=4}

The is Operator

The is operator checks object identity.

One of the clearest beginner examples is checking whether a value is None.

result = None

print(result is None)
True

This is a common and appropriate use of is.

The is not Operator

is not checks that two references do not refer to the same object.

result = None

print(result is not None)
False
Beginner rule:

Use == when you want to compare values.

Use is when you specifically want to test object identity, commonly with None.

Membership Operators

Membership operators are used to check whether something exists inside another object.

The two membership operators are:

  • in
  • not in

Python uses these operators for membership tests. :contentReference[oaicite:5]{index=5}

The in Operator

Let's start with a string.

name = "Olivia"

print("O" in name)
True

Python checks whether the character "O" occurs inside the string.

You can also search for a word:

message = "Welcome to Python"

print("Python" in message)
True

The not in Operator

not in checks whether something does not exist inside another object.

message = "Welcome to Python"

print("Java" not in message)
True

Because the word "Java" is not present in the string.

Membership With a List

Membership testing becomes even more useful when we start working with collections such as lists.

You will learn lists properly in a later section, but here is a small preview:

fruits = ["apple", "orange", "banana"]

print("apple" in fruits)
print("mango" in fruits)
True
False

Python checks whether each item exists in the collection.

Operator Precedence

Sometimes an expression contains several operators.

For example:

result = 10 + 5 * 2

What should Python calculate first?

Multiplication has higher precedence than addition.

Therefore Python calculates:

5 * 2

first, giving:

10 + 10

and the final result is:

20

Python's operator precedence determines which operations are evaluated first. :contentReference[oaicite:6]{index=6}

Use Parentheses When You Want to Be Clear

You can use parentheses to tell Python exactly which calculation should happen first.

result = (10 + 5) * 2

print(result)
30

Without the parentheses:

result = 10 + 5 * 2

print(result)
20
When an expression could confuse a beginner, parentheses often make your intention clearer.

Real-World Example: Trip Fuel Cost

Let's use several concepts together.

Imagine a trip planner needs to calculate fuel cost.

distance = 500
fuel_efficiency = 10
fuel_price = 950

fuel_needed = distance / fuel_efficiency
fuel_cost = fuel_needed * fuel_price

print("Fuel needed:", fuel_needed)
print("Fuel cost:", fuel_cost)
Fuel needed: 50.0
Fuel cost: 47500.0

Notice what happened.

  1. We stored the distance in a variable.
  2. We stored fuel efficiency in another variable.
  3. We divided distance by fuel efficiency.
  4. We multiplied the fuel needed by the fuel price.
  5. We displayed the result.

This is what programming starts to look like: taking simple pieces of information and combining them to solve real problems.

Common Beginner Mistakes

Mistake 1: Confusing = and ==

Remember:

age = 27

assigns a value.

While:

age == 27

compares a value.

Mistake 2: Forgetting that input is text

first = input("First number: ")
second = input("Second number: ")

print(first + second)

This joins strings rather than performing numeric addition.

Mistake 3: Using / when you need //

Regular division:

10 / 3

gives a floating-point result.

Floor division:

10 // 3

gives the floor of the division result.

Mistake 4: Confusing % with percentage

In Python, % is the modulus operator.

It calculates the remainder after division.

Mistake 5: Using is when you mean ==

If you want to compare values, normally use:

==

Use is for identity checks, commonly:

value is None

Practice 1: Basic Arithmetic

Without running the code, try to predict the results:

print(20 + 5)
print(20 - 5)
print(20 * 5)
print(20 / 5)
print(20 // 6)
print(20 % 6)
print(2 ** 4)
Show Answers
25
15
100
4.0
3
2
16

Practice 2: Comparisons

What will each expression produce?

age = 27

print(age == 27)
print(age == 30)
print(age != 30)
print(age > 18)
print(age < 18)
print(age >= 27)
print(age <= 20)
Show Answers
True
False
True
True
False
True
False

Practice 3: Logical Operators

Predict the results:

print(True and True)
print(True and False)
print(True or False)
print(False or False)
print(not True)
print(not False)
Show Answers
True
False
True
False
False
True

Practice 4: Membership

Predict the result:

word = "Python"

print("P" in word)
print("p" in word)
print("Java" in word)
print("Java" not in word)
Show Answers
True
False
False
True
Notice that Python is case-sensitive.

"P" and "p" are different characters.

Mini Project: Simple Shopping Calculator

Build a small program that calculates the total cost of a product.

Your program should:

  1. Store the product price.
  2. Store the quantity.
  3. Multiply them.
  4. Display the total.

For example:

price = 2500
quantity = 4

total = price * quantity

print("Total:", total)

Then improve it by allowing the user to enter the price and quantity.

Challenge:

Add a discount variable and calculate the final amount after the discount.

Quick Quiz: Python Operators

Test yourself before moving to the next section.

1. Which operator is used for addition?

2. What does == do?

3. What does % return?

4. What is the result of 10 // 3?

5. Which operator means "not equal to"?

6. Which operator checks whether something exists inside a collection?

7. Which logical operator requires both conditions to be true?

8. What is the difference between = and ==?

Python Operators: Summary

Operators allow your Python programs to perform calculations, comparisons and logical operations.

Category Important Operators Purpose
Arithmetic + - * / // % ** Mathematical operations
Assignment = += -= *= /= Assign or update values
Comparison == != > < >= <= Compare values
Logical and or not Combine or reverse conditions
Identity is is not Check object identity
Membership in not in Check membership

The Most Important Things to Remember

  1. = assigns a value.
  2. == compares values.
  3. + adds numbers and can join strings.
  4. % gives the remainder after division.
  5. and combines conditions where both need to be true.
  6. or allows at least one condition to be true.
  7. not reverses a truth value.
  8. in checks membership.
  9. is checks object identity.
```html

Python Conditions

In programming, your computer often needs to make decisions. It may need to decide whether a person is old enough to register, whether a student passed an examination, whether there is enough money in an account, or whether a machine should perform a task.

Python allows us to make these decisions using conditions.

Simple idea: A condition asks a question that can be answered with True or False.

What Are Conditions?

A condition is a statement that allows Python to check whether something is true or false.

Think about everyday decisions.

  • If it is raining, take an umbrella.
  • If you are hungry, eat.
  • If your score is 50 or above, you passed.
  • If your battery is low, charge your phone.

Programming works in a similar way. We give the computer a condition and tell it what to do depending on the result.

if condition:
    do something

The important word here is if.

Conditions in Everyday Life

Imagine you are going to travel.

Before leaving, you check the weather.

Your decision might be:

IF it is raining:
    take an umbrella

If it is not raining, you may decide not to take one.

Python allows you to write this type of decision directly into your program.

if raining:
    print("Take an umbrella")

This is the basic idea behind conditions.

Conditions and Boolean Values

Remember that Python has two important Boolean values:

  • True
  • False

Conditions usually produce one of these two values.

print(10 > 5)

Output:

True

Another example:

print(10 < 5)

Output:

False

Python can use these True and False results to decide what your program should do.

Comparison Operators in Conditions

Conditions often use comparison operators.

Operator Meaning Example
== Equal to 5 == 5
!= Not equal to 5 != 3
> Greater than 10 > 5
< Less than 3 < 8
>= Greater than or equal to 10 >= 10
<= Less than or equal to 5 <= 5

These operators allow Python to compare values.

if Statements

The if statement is one of the most important tools for making decisions in Python.

It tells Python:

"Only run this code if this condition is True."

Basic Syntax

if condition:
    statement

For example:

age = 20

if age >= 18:
    print("You are an adult")

Python checks whether age >= 18 is True.

Because 20 is greater than 18, the condition is True, so Python prints:

You are an adult

Breaking Down an if Statement

age = 20

if age >= 18:
    print("You are an adult")

Let's break it down.

  1. age = 20 creates a variable.
  2. if tells Python that a decision is coming.
  3. age >= 18 is the condition.
  4. The colon : tells Python that the block of code begins here.
  5. The indented print() statement is executed if the condition is True.

Indentation in Conditions

Indentation is extremely important in Python.

Python uses indentation to determine which statements belong inside an if block.

age = 25

if age >= 18:
    print("You are an adult")

Notice that print() is moved to the right.

This tells Python that the print statement belongs to the if statement.

Important: Do not forget the colon after the condition.
if age >= 18:
    print("You are an adult")

Using Variables in Conditions

Conditions become much more useful when you combine them with variables.

score = 75

if score >= 50:
    print("You passed")

Python checks the value stored in score.

Since 75 is greater than or equal to 50, the message is displayed.

You can also use strings.

country = "Nigeria"

if country == "Nigeria":
    print("Welcome to Nigeria")

elif Statements

Sometimes you need to check more than one condition.

This is where elif comes in.

elif means:

"If the previous condition was False, check this condition instead."
score = 75

if score >= 80:
    print("Excellent")
elif score >= 50:
    print("Passed")

Python first checks whether the score is 80 or higher.

That is False.

Python then checks whether the score is 50 or higher.

That is True, so Python prints:

Passed

else Statements

The else statement tells Python what to do when the condition is False.

age = 15

if age >= 18:
    print("You are an adult")
else:
    print("You are under 18")

Because the age is 15, the first condition is False. Therefore Python executes the else block.

Output:

You are under 18

An else statement does not have a condition of its own.

It simply handles everything that did not satisfy the previous conditions.

if + elif + else

You can combine all three structures to create a complete decision-making system.

score = 82

if score >= 80:
    print("Excellent")
elif score >= 50:
    print("Passed")
else:
    print("Failed")

Python checks the conditions from top to bottom.

  • If the first condition is True, Python runs it and stops checking the remaining conditions.
  • If it is False, Python checks the next elif.
  • If none of the conditions are True, Python runs else.

The Order of Conditions Matters

Python checks conditions from top to bottom.

This means the order in which you write them matters.

For example:

score = 85

if score >= 50:
    print("Passed")
elif score >= 80:
    print("Excellent")

This will print:

Passed

Why?

Because 85 is already greater than or equal to 50. Python executes the first matching condition and does not continue to the next elif.

A better arrangement is:

score = 85

if score >= 80:
    print("Excellent")
elif score >= 50:
    print("Passed")
else:
    print("Failed")

This prints:

Excellent

Using Multiple elif Statements

You can have several elif statements.

score = 72

if score >= 80:
    print("A")
elif score >= 70:
    print("B")
elif score >= 60:
    print("C")
elif score >= 50:
    print("D")
else:
    print("F")

This is useful when you have several possible outcomes.

Python stops at the first condition that is True.

Combining Conditions

Sometimes one condition is not enough. You may need to check several things at the same time.

Python provides three important logical operators:

  • and
  • or
  • not

The and Operator

and means that all the required conditions must be True.

age = 25
has_ticket = True

if age >= 18 and has_ticket:
    print("You can enter")

Both conditions must be True before the message is displayed.

Think of and as:

"This AND that must both be true."

The or Operator

or means that at least one of the conditions needs to be True.

day = "Saturday"

if day == "Saturday" or day == "Sunday":
    print("It is the weekend")

Only one of the two comparisons needs to be True.

"This OR that can be true."

The not Operator

not reverses a Boolean result.

logged_in = False

if not logged_in:
    print("Please log in")

Because logged_in is False, not logged_in becomes True.

Using Conditions with Strings

Conditions can also compare text.

username = "Olivia"

if username == "Olivia":
    print("Welcome, Olivia")

Remember that Python is case-sensitive.

name = "Olivia"

if name == "olivia":
    print("Welcome")

This condition is False because "Olivia" and "olivia" are different strings.

Using in in Conditions

The in operator can check whether something exists inside another value.

fruits = ["apple", "banana", "orange"]

if "banana" in fruits:
    print("Banana is available")

You can also use it with strings.

message = "Welcome to Python"

if "Python" in message:
    print("Python was mentioned")

Checking for None

Sometimes a variable does not currently contain a value. Python represents this with None.

result = None

if result is None:
    print("There is no result yet")

When checking specifically for None, using is None is the recommended form.

Using Conditions with User Input

Conditions become much more interesting when users can enter information.

age = int(input("Enter your age: "))

if age >= 18:
    print("You are an adult")
else:
    print("You are under 18")

Notice the use of int().

The input() function returns text, so we convert the answer into an integer before comparing it with 18.

Truthy and Falsy Values

Python can also use certain values directly in conditions.

For example, an empty string is considered False in a condition.

name = ""

if name:
    print("Name was entered")
else:
    print("No name was entered")

Because the string is empty, Python treats it as False.

Similarly, an empty list is considered False:

items = []

if items:
    print("There are items")
else:
    print("The list is empty")

You do not need to master truthiness immediately. The important thing at this stage is to understand that Python can evaluate certain values as True or False.

Nested Conditions

A nested condition is an if statement placed inside another if statement.

For example:

age = 25
has_id = True

if age >= 18:
    if has_id:
        print("Entry allowed")

Python first checks the person's age.

If the person is at least 18, Python then checks whether they have an ID.

Nested conditions are useful, but do not use them when a simpler condition would be easier to understand.

The same example can sometimes be written more simply:

if age >= 18 and has_id:
    print("Entry allowed")

Real-World Example: Age Checker

age = int(input("Enter your age: "))

if age >= 18:
    print("You can register.")
else:
    print("You are too young to register.")

This simple program demonstrates how variables, user input, conversion, and conditions can work together.

Real-World Example: Grade Calculator

score = int(input("Enter your score: "))

if score >= 80:
    print("Grade: A")
elif score >= 70:
    print("Grade: B")
elif score >= 60:
    print("Grade: C")
elif score >= 50:
    print("Grade: D")
else:
    print("Grade: F")

This is an excellent example of using multiple conditions to classify information.

Real-World Example: Simple Login Check

Here is a basic learning example of how conditions can compare two pieces of information.

username = input("Username: ")
password = input("Password: ")

if username == "admin" and password == "python123":
    print("Login successful")
else:
    print("Invalid username or password")
Learning example only: Real applications should never store passwords directly in source code like this.

Real-World Example: Agriculture

Conditions become especially useful when programming systems that need to react to sensor information.

Imagine a smart agricultural system measuring soil moisture.

soil_moisture = 25

if soil_moisture < 30:
    print("Soil is dry")
    print("Water the crop")
else:
    print("Soil moisture is sufficient")

This is the beginning of the type of decision-making logic that can later be used in automation and agricultural robotics.

Common Beginner Mistakes

1. Using = instead of ==

Remember:

  • = assigns a value.
  • == compares two values.
age = 18

if age == 18:
    print("Exactly 18")

2. Forgetting the colon

Incorrect:

if age >= 18
    print("Adult")

Correct:

if age >= 18:
    print("Adult")

3. Incorrect indentation

The code inside an if statement must be indented.

if age >= 18:
    print("Adult")

4. Forgetting that input() returns text

This can cause problems:

age = input("Enter age: ")

if age >= 18:
    print("Adult")

A safer beginner approach is:

age = int(input("Enter age: "))

if age >= 18:
    print("Adult")

5. Putting conditions in the wrong order

Remember that Python checks conditions from top to bottom. Always think carefully about which condition should be checked first.

6. Making conditions unnecessarily complicated

If a simple condition can solve the problem, prefer the simple version.

if age >= 18 and has_id:
    print("Allowed")

This may be easier to understand than several deeply nested if statements.

Practice Exercises

Try these exercises yourself before looking for a solution.

Exercise 1 — Positive or Negative

Ask the user for a number and print whether the number is positive, negative, or zero.

Exercise 2 — Age Checker

Ask the user for their age.

  • If the age is below 13, print "Child".
  • If the age is between 13 and 17, print "Teenager".
  • If the age is 18 or above, print "Adult".

Exercise 3 — Student Result

Ask the user for a student's score.

  • 80–100 → Excellent
  • 70–79 → Very Good
  • 60–69 → Good
  • 50–59 → Passed
  • Below 50 → Failed

Exercise 4 — Even or Odd

Ask the user for a number and determine whether it is even or odd.

Hint: Use the modulus operator %.

Exercise 5 — Simple Temperature Checker

Ask the user for a temperature.

  • Above 30 → "Hot"
  • Between 20 and 30 → "Warm"
  • Below 20 → "Cold"

Mini Project: Student Performance Checker

Let's combine what you have learned into a small program.

The program asks for a student's score and determines their performance.

score = int(input("Enter your score: "))

if score >= 80:
    print("Excellent performance!")
elif score >= 70:
    print("Very good performance!")
elif score >= 60:
    print("Good performance!")
elif score >= 50:
    print("You passed.")
else:
    print("You failed. Keep practicing.")

Study this program carefully.

Try changing the score and predict the output before running the program.

Then modify the program yourself. Add another category or change the messages.

Python Conditions Quiz

Test yourself before moving to the next section.

1. Which keyword is used to start a condition?

2. Which operator checks whether two values are equal?

3. What does else do?

4. What does and mean in a condition?

5. Which statement checks whether a number is at least 18?

6. Which keyword allows you to check another condition after if?

7. What does the following print?

score = 40

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

8. What must normally come after an if condition?

Python Conditions Summary

Conditions allow your Python programs to make decisions.

Concept Purpose
if Runs code when a condition is True.
elif Checks another condition if the previous one was False.
else Runs when none of the previous conditions are True.
and Requires multiple conditions to be True.
or Allows at least one condition to be True.
not Reverses a Boolean result.
== Checks whether two values are equal.
> Checks whether one value is greater than another.
< Checks whether one value is less than another.
in Checks whether something exists inside another value.
is None Checks whether a value is None.
Key idea to remember:

Conditions allow your program to make decisions. The computer checks whether something is True or False and then follows the appropriate path.

```html

Python Loops

Imagine you need to print the numbers from 1 to 100. You could write 100 separate print() statements, but that would be slow, repetitive, and difficult to maintain.

Python gives us a better solution: loops.

Simple idea: A loop allows you to repeat a block of code without writing the same code again and again.

What Are Loops?

A loop is a programming structure that repeatedly executes a block of code.

Think about everyday activities.

If someone tells you:

"Keep watering the plants until the soil is wet."

You are being given a repeated task with a condition.

Programming loops work in a similar way.

Instead of writing:

print("Hello")
print("Hello")
print("Hello")
print("Hello")
print("Hello")

You can tell Python to repeat the instruction.

for i in range(5):
    print("Hello")

The result is still five "Hello" messages, but the program is much shorter and easier to maintain.

Types of Loops in Python

Python mainly provides two types of loops:

  • while loop
  • for loop

Both are used for repetition, but they are useful in different situations.

Loop Common Use
while Repeat while a condition remains True.
for Repeat through a sequence or a known range of values.

while Loops

A while loop repeats code as long as a condition is True.

Its basic structure is:

while condition:
    code to repeat

For example:

number = 1

while number <= 5:
    print(number)
    number = number + 1

Output:

1
2
3
4
5

Python keeps checking the condition:

  • Is number less than or equal to 5?
  • If yes, run the code.
  • Increase number by 1.
  • Check the condition again.
  • Stop when the condition becomes False.

Understanding a while Loop Step by Step

number = 1

while number <= 5:
    print(number)
    number = number + 1

Let's understand what happens.

Step 1

Python creates the variable:

number = 1

Step 2

Python checks:

number <= 5

Since 1 is less than or equal to 5, the condition is True.

Step 3

Python prints 1.

Step 4

The number is increased:

number = number + 1

Now number is 2.

Python repeats the process until number becomes 6.

At that point:

6 <= 5

is False, so the loop stops.

while Loops and User Input

One common use of a while loop is to keep asking a user for information until they provide the expected answer.

password = ""

while password != "python":
    password = input("Enter the password: ")

print("Access granted")

The loop continues while the password is incorrect.

Once the user enters python, the condition becomes False and the loop ends.

This is one reason while loops are useful: you may not know exactly how many times the loop needs to run.

Infinite Loops

An infinite loop is a loop that never becomes False.

For example:

number = 1

while number <= 5:
    print(number)

This program never changes number.

Therefore the condition will always remain True.

Be careful: Always make sure a while loop has a way to eventually become False, unless you intentionally want an infinite loop.

The corrected version is:

number = 1

while number <= 5:
    print(number)
    number += 1

for Loops

A for loop is commonly used when you want to repeat something for each item in a sequence or collection.

For example:

fruits = ["apple", "banana", "orange"]

for fruit in fruits:
    print(fruit)

Output:

apple
banana
orange

Python takes each item from the list and temporarily stores it in the variable fruit.

The loop then runs once for each item.

Using a for Loop with a String

Strings are sequences of characters, so you can loop through them one character at a time.

word = "Python"

for letter in word:
    print(letter)

Output:

P
y
t
h
o
n

This demonstrates an important idea: a for loop can move through items one by one.

The range() Function

The range() function is commonly used with for loops when you want to repeat something a specific number of times.

for number in range(5):
    print(number)

Output:

0
1
2
3
4

Notice something important: range(5) starts at 0 and stops before 5.

range(5) produces five numbers: 0, 1, 2, 3, and 4.

range() with a Start and Stop

You can specify where the range should start.

for number in range(1, 6):
    print(number)

Output:

1
2
3
4
5

The first number is the starting point.

The second number is the stopping point, but it is not included.

range() with a Step

You can also tell range() how much to increase the number each time.

for number in range(0, 11, 2):
    print(number)

Output:

0
2
4
6
8
10

The 2 is the step.

It means increase the number by 2 each time.

break

The break statement immediately stops a loop.

for number in range(1, 10):
    if number == 5:
        break

    print(number)

Output:

1
2
3
4

When number becomes 5, Python encounters break and leaves the loop immediately.

Think of break as: "Stop the loop now."

continue

The continue statement skips the current iteration and moves to the next one.

for number in range(1, 6):

    if number == 3:
        continue

    print(number)

Output:

1
2
4
5

The loop did not stop completely. It simply skipped the number 3.

Think of continue as: "Skip this one and keep going."

break vs continue

Keyword What it does
break Stops the entire loop.
continue Skips the current iteration and continues the loop.

A simple way to remember:

  • break = leave the loop.
  • continue = skip this turn.

Nested Loops

A nested loop is a loop inside another loop.

For example:

for outer in range(3):

    for inner in range(2):
        print("Outer:", outer, "Inner:", inner)

The inner loop runs completely for every iteration of the outer loop.

Nested loops are useful for working with things such as:

  • Tables
  • Grids
  • Rows and columns
  • Matrix-like data
  • Repeated combinations

However, nested loops can become difficult to understand, so use them carefully.

Using Loops with Conditions

Loops and conditions are often used together.

For example, suppose you want to print only even numbers.

for number in range(1, 11):

    if number % 2 == 0:
        print(number)

Output:

2
4
6
8
10

Here, the loop produces the numbers and the condition decides which numbers should be printed.

This combination is extremely important in programming.

Loops with User Input

You can use loops to repeatedly ask users for information.

For example:

for i in range(3):
    name = input("Enter your name: ")
    print("Hello", name)

The program asks for a name three times.

This is useful when processing repeated information.

Using Loops to Calculate a Total

One of the most common uses of loops is adding values together.

total = 0

for number in range(1, 6):
    total = total + number

print(total)

Output:

15

The variable total keeps track of the running total.

This pattern is called an accumulator.

You will see this pattern frequently when working with data and real-world problems.

Real-World Uses of Loops

Loops are everywhere in programming.

Processing Student Scores

scores = [70, 85, 62, 91, 55]

for score in scores:
    print(score)

Checking Multiple Crops

crops = ["maize", "rice", "beans"]

for crop in crops:
    print("Checking", crop)

Checking Sensor Readings

soil_moisture_values = [25, 40, 31, 18, 45]

for moisture in soil_moisture_values:

    if moisture < 30:
        print("Soil is dry")

    else:
        print("Soil moisture is sufficient")

This is the kind of basic logic that can later become part of an automated agricultural system.

Common Beginner Mistakes

1. Forgetting to update a while loop

This can create an infinite loop.

number = 1

while number <= 5:
    print(number)

Always make sure the condition can eventually become False.

2. Forgetting the colon

for number in range(5):
    print(number)

The colon is required after the loop statement.

3. Incorrect indentation

for number in range(5):
    print(number)

The code that belongs to the loop must be indented.

4. Forgetting that range() stops before the end value

range(1, 6)

produces 1 through 5, not 1 through 6.

5. Confusing break and continue

Remember:

  • break stops the loop.
  • continue skips the current iteration.

Practice Exercises

Try solving these yourself before checking a solution.

Exercise 1 — Count from 1 to 10

Write a for loop that prints the numbers from 1 to 10.

Exercise 2 — Count Backwards

Write a loop that prints:

10
9
8
7
6
5
4
3
2
1

Exercise 3 — Even Numbers

Print all even numbers between 1 and 20.

Hint: use %.

Exercise 4 — Sum of Numbers

Calculate the sum of numbers from 1 to 100 using a loop.

Exercise 5 — Names

Create a list containing five names and use a for loop to print each name.

Exercise 6 — Password Attempts

Create a program that allows a user to try entering a password up to three times.

Use a loop and a condition.

Exercise 7 — Soil Moisture

Given the following readings:

readings = [20, 35, 28, 45, 18, 50]

Loop through the readings and print:

  • "Dry" when the reading is below 30.
  • "Moist" when the reading is 30 or above.

Mini Project: Number Analyzer

Let's combine loops, conditions, variables, and operators into one small project.

This program examines numbers from 1 to 10 and tells us whether each number is even or odd.

for number in range(1, 11):

    if number % 2 == 0:
        print(number, "is even")

    else:
        print(number, "is odd")

Try changing the range.

Then modify the program so that it also calculates the total of all the numbers.

Python Loops Quiz

Test your understanding before moving forward.

1. What is the main purpose of a loop?

2. Which loop runs while a condition is True?

3. What does range(5) produce?

4. What does break do?

5. What does continue do?

6. Which loop is commonly used to go through items in a list?

7. What happens when a while loop's condition becomes False?

8. What is a nested loop?

Python Loops Summary

Loops allow Python programs to repeat tasks efficiently.

Concept Purpose
while Repeats while a condition is True.
for Loops through a sequence or range.
range() Generates a sequence of numbers.
break Stops the loop immediately.
continue Skips the current iteration.
Nested loop A loop placed inside another loop.
Key idea to remember:

Use a for loop when you are generally moving through a collection or a known range. Use a while loop when repetition depends on a condition remaining True.

```html

Python Strings

Strings are one of the most commonly used data types in Python. Whenever your program needs to work with text, you will probably be working with strings.

Names, usernames, messages, addresses, sentences, email addresses and even pieces of code can all be represented as strings.

Simple idea: A string is a sequence of characters used to represent text.

What Is a String?

A string is text surrounded by quotation marks.

For example:

"Hello"

This is a string because Hello is surrounded by quotation marks.

You can also store a string inside a variable:

name = "Olivia"

print(name)

Output:

Olivia

Strings can contain letters, numbers, spaces and special characters.

"Python"
"Hello World"
"12345"
"olivia@example.com"
"Welcome to Gabbywall!"

Even though "12345" contains numbers, it is still a string because it is surrounded by quotation marks.

Creating Strings

Python allows you to create strings using either single quotation marks or double quotation marks.

name = "Olivia"

Or:

name = 'Olivia'

Both are valid.

The important thing is to make sure that the quotation marks are properly matched.

message = "Hello"
city = 'Enugu'

Single Quotes vs Double Quotes

Python does not treat single and double quotation marks as different types of strings.

name = "Olivia"

name = 'Olivia'

Both create a string.

You can choose whichever style you prefer, but consistency is important when writing larger programs.

Sometimes one type of quotation mark is useful when the text itself contains another type.

message = "I'm learning Python"

print(message)

Here, double quotes allow the apostrophe in I'm to appear normally.

You could also write:

message = 'She said "Hello"'

Multiline Strings

Sometimes you want a string to contain multiple lines.

Python allows you to create multiline strings using triple quotation marks.

message = """Welcome to Gabbywall.

This is a Python course.

Keep learning!"""

print(message)

Triple quotes can be written using either single quotes or double quotes.

text = '''This is
a multiline
string.'''

Multiline strings are useful when working with longer pieces of text.

Finding the Length of a String

Python provides the len() function to determine how many characters are inside a string.

name = "Python"

print(len(name))

Output:

6

Python contains six characters:

P y t h o n

Spaces are also counted as characters.

message = "Hello World"

print(len(message))

The space between "Hello" and "World" is included in the count.

Accessing Characters in a String

Strings are sequences of characters. Each character has a position called an index.

Python starts counting positions from 0, not 1.

word = "Python"

The positions are:

P   y   t   h   o   n
0   1   2   3   4   5

You can access a character using square brackets.

word = "Python"

print(word[0])

Output:

P

Another example:

print(word[3])

Output:

h
Remember: Python starts indexing at 0.

Negative Indexing

Python also allows you to access characters from the end of a string using negative indexes.

word = "Python"

print(word[-1])

Output:

n

The last character is -1.

P   y   t   h   o   n
-6  -5  -4  -3  -2  -1

This can be useful when you want to access the end of a string without knowing its exact length.

Slicing Strings

Slicing allows you to take a portion of a string.

The basic syntax is:

string[start:stop]

For example:

word = "Python"

print(word[0:3])

Output:

Pyt

The starting position is included, but the stopping position is not included.

So 0:3 means positions 0, 1 and 2.

Slicing from the Beginning

You can leave out the starting position.

word = "Python"

print(word[:3])

Output:

Pyt

Python automatically starts from the beginning.

Slicing to the End

You can also leave out the stopping position.

word = "Python"

print(word[2:])

Output:

thon

Python takes everything from position 2 to the end.

String Methods

Python provides many built-in methods for working with strings.

A method is an action that can be performed on a value.

For example:

name = "olivia"

print(name.upper())

Output:

OLIVIA

Notice the dot:

name.upper()

The upper() method changes the string to uppercase in the returned result.

The original string itself is not changed.

upper() and lower()

upper() converts letters to uppercase.

text = "hello"

print(text.upper())

Output:

HELLO

lower() converts letters to lowercase.

text = "HELLO"

print(text.lower())

Output:

hello

These methods are especially useful when comparing user input.

answer = input("Continue? ")

if answer.lower() == "yes":
    print("Continuing...")

Now inputs such as YES, Yes and yes can all be handled the same way.

strip()

The strip() method removes unnecessary whitespace from the beginning and end of a string.

name = "   Olivia   "

print(name.strip())

Output:

Olivia

This is useful when processing information entered by users.

name = input("Enter your name: ").strip()

print("Hello", name)

replace()

The replace() method replaces one piece of text with another.

message = "I love Java"

new_message = message.replace("Java", "Python")

print(new_message)

Output:

I love Python

Again, the original string is not changed automatically. The result is returned and can be stored in another variable.

split()

The split() method divides a string into smaller pieces and returns them as a list.

sentence = "Python is easy"

words = sentence.split()

print(words)

Output:

['Python', 'is', 'easy']

By default, Python separates the text at whitespace.

We will study lists in much more detail later in this course.

join()

The join() method does the opposite of split() in many common situations.

It can combine multiple strings into one string.

words = ["Python", "is", "powerful"]

sentence = " ".join(words)

print(sentence)

Output:

Python is powerful

The string before .join() determines what separates the items.

find()

The find() method searches for a piece of text inside another string.

text = "I am learning Python"

position = text.find("Python")

print(position)

Python returns the position where the word begins.

If the text cannot be found, find() returns -1.

count()

The count() method tells you how many times a particular piece of text appears.

text = "banana"

print(text.count("a"))

Output:

3

startswith() and endswith()

These methods allow you to check how a string begins or ends.

filename = "photo.jpg"

print(filename.startswith("photo"))

Output:

True

You can also check how it ends:

print(filename.endswith(".jpg"))

Output:

True

Combining Strings

Combining strings together is called concatenation.

You can use the + operator.

first_name = "Olivia"
last_name = "Ezeani"

full_name = first_name + " " + last_name

print(full_name)

Output:

Olivia Ezeani

Notice that we added a space between the two names.

Strings and Numbers

Be careful when combining strings and numbers.

This will cause an error:

age = 27

print("I am " + age + " years old")

Why?

Because age is an integer while the other values are strings.

One solution is to convert the number into a string:

age = 27

print("I am " + str(age) + " years old")

However, there is an easier and more modern approach: f-strings.

f-Strings

f-strings provide a convenient way to insert variables directly into a string.

name = "Olivia"
age = 27

print(f"My name is {name} and I am {age} years old.")

Output:

My name is Olivia and I am 27 years old.

The f before the quotation mark tells Python that the string is an f-string.

Variables can be placed inside curly braces:

{name}
{age}

f-strings are one of the most useful ways to create dynamic text in Python.

Escape Characters

Sometimes you need to place special characters inside a string.

Python uses the backslash \ for many escape sequences.

New Line

print("Hello\nPython")

Output:

Hello
Python

Tab

print("Name:\tOlivia")

The \t creates a tab space.

Quotation Marks

You can use a backslash when you need quotation marks inside a string that use the same quote style.

message = "She said \"Hello\""

print(message)

Strings Cannot Be Changed Directly

Python strings are immutable.

This means you cannot change an individual character directly after the string has been created.

For example, this does not work:

word = "Python"

word[0] = "J"

Instead, you create a new string.

word = "Python"

word = "J" + word[1:]

print(word)

Output:

Jython

You do not need to memorize the technical meaning of "immutable" yet. Just remember that individual characters in a string cannot be replaced directly.

Checking Strings

Python provides useful methods for checking the contents of strings.

isalpha()

Checks whether all characters are letters.

text = "Python"

print(text.isalpha())

Output:

True

isdigit()

Checks whether all characters are digits.

text = "12345"

print(text.isdigit())

Output:

True

isalnum()

Checks whether the string contains only letters and numbers.

text = "Python123"

print(text.isalnum())

Output:

True

Strings and User Input

The input() function returns a string.

name = input("Enter your name: ")

print("Hello", name)

Even if the user enters a number, input() initially gives you a string.

age = input("Enter your age: ")

print(type(age))

If the user enters 27, Python still treats the result as:

<class 'str'>

If you need a number, convert it:

age = int(input("Enter your age: "))

Real-World Example: Cleaning User Input

Imagine a program asking a user to enter their country.

country = input("Enter your country: ")

country = country.strip().lower()

if country == "nigeria":
    print("Welcome!")

The program uses two string methods:

  • strip() removes unnecessary spaces.
  • lower() converts the text to lowercase.

This makes the program more tolerant of how the user enters the information.

Common Beginner Mistakes

1. Forgetting quotation marks

Incorrect:

name = Olivia

Correct:

name = "Olivia"

2. Confusing a number with a numeric string

age = 27

This is an integer.

age = "27"

This is a string.

3. Forgetting that indexes start at 0

word = "Python"

print(word[0])

The result is P, not y.

4. Trying to change a character directly

word = "Python"

word[0] = "J"

Strings are immutable, so this will produce an error.

5. Forgetting that methods use parentheses

Correct:

name.upper()

The parentheses are part of calling the method.

Practice Exercises

Try these exercises yourself before checking a solution.

Exercise 1 — Your Name

Create a variable containing your name and print it.

Exercise 2 — String Length

Create a string and use len() to find its length.

Exercise 3 — First Character

Create a word and print its first character using indexing.

Exercise 4 — Last Character

Print the last character of a string using negative indexing.

Exercise 5 — Uppercase

Ask the user for their name and print it in uppercase.

Exercise 6 — Clean Input

Ask the user to enter their name with possible spaces around it. Remove those spaces using strip().

Exercise 7 — Replace Text

Create the string:

"I am learning Java"

Replace Java with Python.

Exercise 8 — Word Counter

Ask the user to enter a sentence and use split() to separate the words.

Exercise 9 — f-String

Create variables for your name, age and country. Use an f-string to print them in one sentence.

Mini Project: Personal Profile

Let's combine several string concepts into one small project.

name = input("Enter your name: ").strip()
country = input("Enter your country: ").strip()
profession = input("Enter your profession: ").strip()

print()
print("----- PROFILE -----")
print(f"Name: {name}")
print(f"Country: {country}")
print(f"Profession: {profession}")

This project uses:

  • input()
  • strip()
  • variables
  • strings
  • f-strings
  • print()

Try improving the project by adding your age, favorite programming language and a short description.

Python Strings Quiz

Test yourself before moving to the next topic.

1. What is a string?

2. Which is a valid string?

3. What index represents the first character in a Python string?

4. Which function finds the length of a string?

5. What does upper() do?

6. What does strip() commonly remove?

7. Which symbol is commonly used to concatenate strings?

8. What does the following produce?

name = "Python"
print(name[-1])

Python Strings Summary

Strings are used whenever your program needs to work with text.

Concept Purpose
String Represents text.
len() Finds the number of characters.
Indexing Accesses individual characters.
Slicing Extracts part of a string.
upper() Converts text to uppercase.
lower() Converts text to lowercase.
strip() Removes surrounding whitespace.
replace() Replaces text with other text.
split() Breaks a string into pieces.
join() Combines strings together.
find() Searches for text inside a string.
count() Counts occurrences of text.
f-string Inserts variables into text easily.
Key idea to remember:

Strings are sequences of characters. You can access, search, slice, combine and transform them using Python's string operations and methods.

```html

Python Lists

Lists are one of the most important data structures you will learn in Python. They allow you to store multiple values inside a single variable.

Imagine you want to store the names of five students. You could create five different variables, but that would quickly become difficult to manage.

student1 = "Ada"
student2 = "John"
student3 = "Mary"
student4 = "David"
student5 = "Sarah"

A list gives you a much better way to organize those values:

students = ["Ada", "John", "Mary", "David", "Sarah"]
Simple idea: A list is a collection that allows you to store multiple items in one variable.

What Is a List?

A list is a collection of items written inside square brackets [].

fruits = ["apple", "banana", "orange"]

This list contains three items.

Lists can contain different types of data.

items = ["Python", 27, 3.14, True]

Although it is possible to mix data types in a list, it is usually clearer to use lists for related information.

Creating a List

To create a list, place your items between square brackets.

colors = ["red", "green", "blue"]

You can also create an empty list.

students = []

An empty list contains no items yet.

You can add items to it later.

Items in a List

Each value stored inside a list is called an item or element.

fruits = ["apple", "banana", "orange"]

The items are:

  • apple
  • banana
  • orange

Lists can store strings, numbers, Boolean values and even other collections.

scores = [75, 82, 91, 68, 88]

passed = [True, True, True, False, True]

Lists Are Ordered

Items in a Python list have a specific order.

fruits = ["apple", "banana", "orange"]

Python remembers the order in which the items appear.

This means that apple is first, banana is second and orange is third.

This becomes important when accessing items by their index.

Accessing List Items

Just like strings, lists use indexes.

Python starts counting from 0.

fruits = ["apple", "banana", "orange"]

The positions are:

apple     banana     orange
  0          1          2

To get the first item:

print(fruits[0])

Output:

apple

To get the second item:

print(fruits[1])

Output:

banana

Negative Indexing

You can also access list items from the end using negative indexes.

fruits = ["apple", "banana", "orange"]
print(fruits[-1])

Output:

orange

The positions are:

apple      banana      orange
-3           -2          -1

Changing List Items

One of the important differences between lists and strings is that lists are mutable.

This means you can change individual items after creating the list.

fruits = ["apple", "banana", "orange"]

fruits[1] = "mango"

print(fruits)

Output:

['apple', 'mango', 'orange']

We replaced banana with mango.

Changing Multiple List Items

You can change several items at once using slicing.

colors = ["red", "green", "blue", "yellow"]

colors[1:3] = ["black", "white"]

print(colors)

Output:

['red', 'black', 'white', 'yellow']

Finding the Length of a List

The len() function tells you how many items are in a list.

fruits = ["apple", "banana", "orange"]

print(len(fruits))

Output:

3

Notice that len() tells you the number of items, while indexing tells you the position of an item.

Adding Items with append()

The append() method adds one item to the end of a list.

fruits = ["apple", "banana"]

fruits.append("orange")

print(fruits)

Output:

['apple', 'banana', 'orange']

This is especially useful when you do not know all the items when the program starts.

Adding Items with insert()

The insert() method allows you to add an item at a specific position.

fruits = ["apple", "orange"]

fruits.insert(1, "banana")

print(fruits)

Output:

['apple', 'banana', 'orange']

The first argument is the position and the second argument is the item you want to add.

Adding Multiple Items with extend()

The extend() method adds multiple items from another collection.

fruits = ["apple", "banana"]

more_fruits = ["orange", "mango"]

fruits.extend(more_fruits)

print(fruits)

Output:

['apple', 'banana', 'orange', 'mango']

The difference is simple:

  • append() adds one item.
  • extend() adds items from another collection.

Removing Items with remove()

The remove() method removes an item by its value.

fruits = ["apple", "banana", "orange"]

fruits.remove("banana")

print(fruits)

Output:

['apple', 'orange']

If the specified value does not exist, Python raises an error.

Removing Items with pop()

The pop() method removes an item using its index.

fruits = ["apple", "banana", "orange"]

fruits.pop(1)

print(fruits)

Output:

['apple', 'orange']

If you use pop() without an index, Python removes the last item.

fruits = ["apple", "banana", "orange"]

fruits.pop()

print(fruits)

Output:

['apple', 'banana']

Deleting Items with del

You can use the del statement to delete an item using its index.

fruits = ["apple", "banana", "orange"]

del fruits[1]

print(fruits)

Output:

['apple', 'orange']

You can also delete the entire list.

del fruits

Removing Everything with clear()

The clear() method removes all items from a list but leaves the list itself available.

fruits = ["apple", "banana", "orange"]

fruits.clear()

print(fruits)

Output:

[]

Slicing Lists

You can extract part of a list using slicing.

numbers = [10, 20, 30, 40, 50]

print(numbers[1:4])

Output:

[20, 30, 40]

Just like strings, the starting index is included and the ending index is not included.

You can also leave out the starting or ending position.

numbers[:3]
numbers[2:]
numbers[:]

Checking Whether an Item Exists

You can use the in operator to check whether an item exists in a list.

fruits = ["apple", "banana", "orange"]

print("banana" in fruits)

Output:

True

You can also use not in.

print("mango" not in fruits)

Output:

True

Sorting a List

The sort() method sorts items in ascending order by default.

numbers = [50, 10, 40, 20, 30]

numbers.sort()

print(numbers)

Output:

[10, 20, 30, 40, 50]

You can sort numbers from largest to smallest using reverse=True.

numbers.sort(reverse=True)

print(numbers)

Output:

[50, 40, 30, 20, 10]

Reversing a List

The reverse() method reverses the current order of the items.

numbers = [1, 2, 3, 4, 5]

numbers.reverse()

print(numbers)

Output:

[5, 4, 3, 2, 1]

Copying a List

You can make a copy of a list using the copy() method.

fruits = ["apple", "banana", "orange"]

new_fruits = fruits.copy()

print(new_fruits)

This creates another list containing the same items.

Counting Items

The count() method tells you how many times a value appears in a list.

numbers = [1, 2, 2, 3, 2, 4]

print(numbers.count(2))

Output:

3

Finding an Item's Position

The index() method returns the position of an item.

fruits = ["apple", "banana", "orange"]

print(fruits.index("banana"))

Output:

1

Looping Through a List

Lists become especially powerful when combined with loops.

fruits = ["apple", "banana", "orange"]

for fruit in fruits:
    print(fruit)

Output:

apple
banana
orange

The loop takes each item from the list one at a time.

This is one of the most common patterns you will use in Python.

Lists with Conditions

You can combine lists with if statements.

scores = [45, 72, 81, 39, 90]

for score in scores:

    if score >= 50:
        print(score, "Pass")
    else:
        print(score, "Fail")

This allows your program to process many values automatically.

List Comprehension

Python provides a shorter way to create lists from existing sequences called a list comprehension.

For example, suppose you want the squares of numbers 1 through 5.

You could write:

squares = []

for number in range(1, 6):
    squares.append(number * number)

print(squares)

A list comprehension can express the same idea more compactly:

squares = [number * number for number in range(1, 6)]

print(squares)

Output:

[1, 4, 9, 16, 25]

Do not worry if this looks unfamiliar. The important thing is to understand normal lists and loops first.

Lists Inside Lists

A list can contain other lists. These are sometimes called nested lists.

students = [
    ["Ada", 85],
    ["John", 72],
    ["Mary", 91]
]

You can access an item from the outer list first:

print(students[0])

Output:

['Ada', 85]

You can then access an item inside that inner list:

print(students[0][0])

Output:

Ada

Nested lists are useful for representing structured data, although dictionaries and other data structures may sometimes be a better choice.

Real-World Example: Student Scores

Imagine you are building a student performance program.

scores = [72, 85, 64, 91, 78]

print("Number of scores:", len(scores))
print("Highest score:", max(scores))
print("Lowest score:", min(scores))
print("Total score:", sum(scores))

Output:

Number of scores: 5
Highest score: 91
Lowest score: 64
Total score: 390

Python provides useful built-in functions such as:

  • len() — number of items
  • max() — largest value
  • min() — smallest value
  • sum() — total of numeric values

Common Beginner Mistakes

1. Forgetting square brackets

Incorrect:

fruits = "apple", "banana", "orange"

Python can interpret this as a tuple rather than a list.

For a list, use:

fruits = ["apple", "banana", "orange"]

2. Forgetting that indexes start at 0

fruits = ["apple", "banana", "orange"]

print(fruits[0])

The first item is at index 0.

3. Using an index that does not exist

fruits = ["apple", "banana"]

print(fruits[5])

This causes an IndexError because index 5 does not exist.

4. Confusing remove() and pop()

remove() removes by value:

fruits.remove("banana")

pop() removes by index:

fruits.pop(1)

5. Forgetting that methods can modify a list

Methods such as sort(), reverse(), append() and remove() change the list itself.

Practice Exercises

Try solving these exercises yourself before looking for help.

Exercise 1 — Create a List

Create a list containing five of your favorite foods.

Exercise 2 — Access Items

Print the first, second and last items in your list.

Exercise 3 — Change an Item

Replace one item in your list with another item.

Exercise 4 — Add an Item

Add a new item using append().

Exercise 5 — Insert an Item

Insert a new item at position 1.

Exercise 6 — Remove an Item

Remove one item using remove().

Exercise 7 — Count Items

Create a list containing repeated values and use count().

Exercise 8 — Sort Numbers

Create a list of numbers and sort them from smallest to largest.

Exercise 9 — Loop Through a List

Create a list of names and use a for loop to print each name.

Exercise 10 — Student Scores

Create a list containing five student scores. Calculate the total, average, highest and lowest scores.

Mini Project: Simple Shopping List

Let's build a simple shopping list using the concepts you have learned.

shopping_list = []

shopping_list.append("Rice")
shopping_list.append("Milk")
shopping_list.append("Bread")
shopping_list.append("Eggs")

print("Shopping List:")

for item in shopping_list:
    print("-", item)

Output:

Shopping List:
- Rice
- Milk
- Bread
- Eggs

Now improve the program.

Try allowing the user to enter their own items.

shopping_list = []

item = input("Enter an item: ")

shopping_list.append(item)

print(shopping_list)

Later, when you understand loops and conditions better, you can turn this into a complete shopping-list application that allows users to add, remove and view items.

Python Lists Quiz

Test your understanding before continuing.

1. Which brackets are used to create a list?

2. What is the index of the first list item?

3. Which method adds an item to the end of a list?

4. Which method removes an item by its value?

5. What does len() return for a list?

6. Which method sorts a list?

7. What does pop() normally remove when no index is provided?

8. What does "apple" in fruits check?

"apple" in fruits

Python Lists Summary

Lists allow you to store and manage multiple values inside one variable.

Concept Purpose
List Stores multiple items in one collection.
Index Identifies the position of an item.
len() Returns the number of items.
append() Adds an item to the end.
insert() Adds an item at a specific position.
extend() Adds items from another collection.
remove() Removes an item by value.
pop() Removes an item by index.
clear() Removes all items.
sort() Sorts the list.
reverse() Reverses the list order.
count() Counts how many times a value appears.
index() Finds the position of an item.
in Checks whether an item exists.
Key idea:

A list lets you keep many related values together and gives you tools for adding, removing, changing, searching, sorting and processing those values.

```html

Python Tuples

In the previous lesson, you learned about Python lists. Lists are useful when you need a collection of values that you may want to change.

Now we are going to learn about another Python collection: tuples.

Tuples look very similar to lists, but there is one major difference:

Important: Tuples cannot be changed after they are created.

This property makes tuples useful when you want to store related information that should remain unchanged.


What Is a Tuple?

A tuple is a collection of items that is ordered and cannot be changed after it has been created.

Tuples are normally written using parentheses:

fruits = ("apple", "banana", "orange")

This tuple contains three items.

Like lists, tuple indexes begin at 0.

Tuple vs List

The easiest way to understand tuples is to compare them with lists.

my_list = ["apple", "banana", "orange"]

my_tuple = ("apple", "banana", "orange")

The list uses square brackets:

["apple", "banana", "orange"]

The tuple uses parentheses:

("apple", "banana", "orange")
Feature List Tuple
Syntax [] ()
Ordered Yes Yes
Changeable Yes No
Allows duplicates Yes Yes
Indexing Yes Yes

Creating a Tuple

Creating a tuple is straightforward.

colors = ("red", "green", "blue")

You can also create a tuple containing numbers:

numbers = (10, 20, 30, 40)

You can create a tuple containing different data types:

person = ("Olivia", 27, True, 1.75)

However, it is usually best to group related information together.

Creating an Empty Tuple

You can create an empty tuple using empty parentheses.

my_tuple = ()

The tuple currently contains no items.

Creating a Tuple with One Item

There is an important rule when creating a tuple containing only one item.

You need a comma after the item.

fruits = ("apple",)

Without the comma, Python treats it as an ordinary value inside parentheses.

fruits = ("apple")

The second example is simply a string, not a tuple.

You can confirm this with type().

print(type(("apple",)))

print(type(("apple")))

Accessing Tuple Items

Tuples use indexes just like lists and strings.

fruits = ("apple", "banana", "orange")

print(fruits[0])

Output:

apple

The second item is at index 1:

print(fruits[1])

Output:

banana

Negative Indexing

You can access items from the end of a tuple using negative indexes.

fruits = ("apple", "banana", "orange")

print(fruits[-1])

Output:

orange

The positions are:

apple       banana       orange
-3            -2           -1

Finding the Length of a Tuple

Use len() to find the number of items in a tuple.

fruits = ("apple", "banana", "orange")

print(len(fruits))

Output:

3

Slicing Tuples

You can extract part of a tuple using slicing.

numbers = (10, 20, 30, 40, 50)

print(numbers[1:4])

Output:

(20, 30, 40)

You can also leave out the starting or ending index.

numbers[:3]

numbers[2:]

numbers[:]

Tuples Cannot Be Changed

This is the most important thing to understand about tuples.

Once a tuple has been created, you cannot change an individual item.

fruits = ("apple", "banana", "orange")

fruits[1] = "mango"

This produces an error because tuples are immutable.

Immutable means that the object cannot be changed in the way you are attempting after it has been created.

This does not mean that Python can never create another tuple containing different values. It means the existing tuple itself cannot have its items changed.

Why Use Tuples?

If lists can store multiple values, you may wonder: why do we need tuples?

Tuples are useful when the values should remain fixed.

For example, suppose you want to store the coordinates of a fixed location:

coordinates = (6.5244, 3.3792)

Or the dimensions of an object:

dimensions = (1920, 1080)

Or the days of the week:

days = (
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday"
)

These are examples of information that your program may want to treat as a fixed collection.

Checking Whether an Item Exists

You can use in with tuples.

fruits = ("apple", "banana", "orange")

print("banana" in fruits)

Output:

True

You can also use not in.

print("mango" not in fruits)

Output:

True

Looping Through a Tuple

Tuples can be used with for loops.

fruits = ("apple", "banana", "orange")

for fruit in fruits:
    print(fruit)

Output:

apple
banana
orange

Python takes each item from the tuple one at a time.

Counting Items with count()

The count() method tells you how many times a value appears in a tuple.

numbers = (1, 2, 2, 3, 2, 4)

print(numbers.count(2))

Output:

3

Finding an Item with index()

The index() method returns the position of a specified item.

fruits = ("apple", "banana", "orange")

print(fruits.index("banana"))

Output:

1

Converting Between Lists and Tuples

You can convert a list into a tuple using tuple().

fruits = ["apple", "banana", "orange"]

fruits_tuple = tuple(fruits)

print(fruits_tuple)

Output:

('apple', 'banana', 'orange')

You can also convert a tuple into a list using list().

fruits = ("apple", "banana", "orange")

fruits_list = list(fruits)

print(fruits_list)

This can be useful when you need to temporarily work with a collection in a changeable form.

Tuple Unpacking

Tuple unpacking allows you to assign the values in a tuple to separate variables.

person = ("Olivia", 27, "Python")

name, age, language = person

print(name)
print(age)
print(language)

Output:

Olivia
27
Python

Python takes the first tuple item and assigns it to name, the second to age, and the third to language.

Important: The number of variables normally needs to match the number of values being unpacked.

Using * During Unpacking

Python also allows an asterisk to collect multiple remaining values into a list during unpacking.

numbers = (10, 20, 30, 40, 50)

first, *middle, last = numbers

print(first)
print(middle)
print(last)

Output:

10
[20, 30, 40]
50

The *middle variable collects the values that remain between the first and last values.

Nested Tuples

A tuple can contain other tuples.

students = (
    ("Ada", 85),
    ("John", 72),
    ("Mary", 91)
)

You can access the first inner tuple:

print(students[0])

Output:

('Ada', 85)

You can then access the student's name:

print(students[0][0])

Output:

Ada

Tuples Can Contain Different Data Types

A tuple can contain different types of values.

data = ("Olivia", 27, 85.5, True)

Here we have:

  • A string
  • An integer
  • A float
  • A Boolean value

You can also have a tuple containing another collection.

data = ("Python", [1, 2, 3], True)

Real-World Examples of Tuples

1. Coordinates

location = (6.5244, 3.3792)

A coordinate pair is naturally represented as two related values that belong together.

2. RGB Values

rgb = (255, 128, 0)

The values represent a color using red, green and blue components.

3. Dimensions

screen = (1920, 1080)

This could represent width and height.

4. Fixed Configuration

robot_dimensions = (40, 25, 15)

A robotics program could use a tuple to represent fixed dimensions such as width, height and depth.

5. Agricultural Coordinates

field_position = (12.45, 8.72)

A program could use a tuple to represent a fixed position within a field or coordinate system.

Common Beginner Mistakes

1. Trying to change a tuple

colors = ("red", "green", "blue")

colors[0] = "black"

This does not work because tuples are immutable.

2. Forgetting the comma in a one-item tuple

Correct:

item = ("Python",)

Not a tuple:

item = ("Python")

3. Confusing tuple syntax with list syntax

my_list = [1, 2, 3]

my_tuple = (1, 2, 3)

Remember:

  • [] → list
  • () → tuple

4. Unpacking the wrong number of values

numbers = (10, 20, 30)

a, b = numbers

This produces an error because there are three values but only two variables.

Practice Exercises

Exercise 1 — Create a Tuple

Create a tuple containing five countries.

Exercise 2 — Access Items

Print the first and last items in your tuple.

Exercise 3 — Find Length

Use len() to find the number of items.

Exercise 4 — Membership

Use in to check whether a particular country exists in your tuple.

Exercise 5 — Count

Create a tuple containing repeated numbers and use count().

Exercise 6 — Index

Use index() to find the position of an item.

Exercise 7 — Unpacking

Create a tuple containing your name, age and favorite programming language. Unpack the values into three variables.

Exercise 8 — Coordinates

Create a tuple containing two numbers representing an imaginary location.

Mini Project: Student Record

Let's use a tuple to store a simple student record.

student = ("Ada", 85, "Biology")

name, score, subject = student

print("Student:", name)
print("Score:", score)
print("Subject:", subject)

Output:

Student: Ada
Score: 85
Subject: Biology

The tuple keeps the three related values together, while unpacking makes them easy to work with individually.

Try creating your own student record.

Python Tuples Quiz

Test what you have learned.

1. Which brackets are commonly used to create a tuple?

2. What is the most important difference between a list and a tuple?

3. What is the index of the first tuple item?

4. Which function returns the number of items?

5. Which is a correct one-item tuple?

6. Which method counts how many times a value appears?

7. What does tuple unpacking allow you to do?

8. Which operator checks whether an item exists in a tuple?

Python Tuples Summary

A tuple is an ordered collection that cannot be changed after it has been created.

Concept Purpose
Tuple Stores an ordered collection of values.
() Common syntax used to create tuples.
Indexing Accesses individual tuple items.
Negative indexing Accesses items from the end.
len() Returns the number of items.
Slicing Extracts part of a tuple.
count() Counts occurrences of a value.
index() Finds the position of a value.
in Checks whether a value exists.
Unpacking Assigns tuple values to variables.
Immutable Tuple items cannot be changed after creation.
Key idea:

Use a list when you expect the collection to change. Use a tuple when the collection should remain fixed.

```html

Python Sets

You have already learned about lists and tuples. Both allow you to store multiple values in a single variable.

Now we will learn about another Python collection called a set.

A set is useful when you want to store a collection of unique values.

Simple idea: A set is a collection that does not allow duplicate items.

For example, suppose you have:

numbers = [1, 2, 2, 3, 3, 3, 4]

There are duplicate values in this list. A set can automatically keep only the unique values:

numbers = {1, 2, 2, 3, 3, 3, 4}

print(numbers)

The result contains each value only once.

{1, 2, 3, 4}

What Is a Set?

A set is a collection of unique items.

Sets are written using curly braces:

fruits = {"apple", "banana", "orange"}

Unlike lists and tuples, sets do not use numeric indexes to access individual items.

The main reason to use a set is to work with unique values and perform operations such as union, intersection and difference.

Set vs List vs Tuple

It is important to understand how sets differ from the collections you have already learned.

Feature List Tuple Set
Syntax [] () {}
Ordered Yes Yes No guaranteed order
Changeable Yes No Yes
Duplicates Allowed Allowed Not allowed
Indexing Yes Yes No

Creating a Set

You can create a set by placing values inside curly braces.

colors = {"red", "green", "blue"}

You can also create a set containing numbers:

numbers = {10, 20, 30, 40}

Sets can contain values of different data types, provided those values are suitable for use in a set.

data = {"Python", 27, True}

Sets Do Not Allow Duplicates

This is one of the most important characteristics of a set.

numbers = {1, 2, 2, 3, 3, 4}

print(numbers)

Python keeps only one copy of each value.

This makes sets very useful for removing duplicates.

names = ["Ada", "John", "Ada", "Mary", "John"]

unique_names = set(names)

print(unique_names)

The resulting set contains each name only once.

Creating an Empty Set

There is an important detail when creating an empty set.

You cannot use just {} because Python interprets that as an empty dictionary.

empty = {}

This creates a dictionary, not a set.

To create an empty set, use set():

empty = set()

print(type(empty))

Output:

<class 'set'>

Sets Are Unordered

Sets do not provide a sequence position like lists and tuples.

Therefore, you should not depend on a particular display order when working with a set.

fruits = {"apple", "banana", "orange"}

You should not assume that the items will always be displayed in the order you wrote them.

Remember: If the order of items matters, a list or tuple may be more appropriate.

Accessing Items in a Set

Because sets do not use indexes, you cannot do this:

fruits = {"apple", "banana", "orange"}

print(fruits[0])

That will produce an error.

Instead, you can loop through the set.

for fruit in fruits:
    print(fruit)

You can also check whether an item exists using in.

print("apple" in fruits)

Adding Items with add()

The add() method adds one item to a set.

fruits = {"apple", "banana"}

fruits.add("orange")

print(fruits)

The set now contains the new value.

If you attempt to add a value that is already present, the set remains unchanged.

fruits.add("apple")

There will still be only one "apple".

Adding Multiple Items with update()

Use update() when you want to add multiple items from another collection.

fruits = {"apple", "banana"}

more_fruits = {"orange", "mango"}

fruits.update(more_fruits)

print(fruits)

You can also update a set using a list:

fruits.update(["pawpaw", "watermelon"])

Any duplicate values are automatically ignored.

Removing Items with remove()

The remove() method removes a specific item.

fruits = {"apple", "banana", "orange"}

fruits.remove("banana")

print(fruits)

If the item does not exist, remove() raises a KeyError.

Removing Items with discard()

The discard() method also removes an item.

fruits = {"apple", "banana", "orange"}

fruits.discard("banana")

The important difference is what happens if the item does not exist.

discard() does not raise an error when the value is missing.

fruits.discard("mango")

The program continues normally.

Quick difference: remove() expects the item to exist. discard() safely does nothing if it does not.

Removing an Item with pop()

Sets also have a pop() method.

However, unlike list pop(), you cannot use an index with set pop().

fruits = {"apple", "banana", "orange"}

removed = fruits.pop()

print(removed)
print(fruits)

The item removed is not something you should predict from the set's written order.

Removing Everything with clear()

The clear() method removes all items from a set.

fruits = {"apple", "banana", "orange"}

fruits.clear()

print(fruits)

Output:

set()

Deleting a Set with del

The del statement can delete the entire set.

fruits = {"apple", "banana", "orange"}

del fruits

After this, the variable fruits no longer exists.

Checking Membership

The in operator is especially useful with sets.

countries = {"Nigeria", "Ghana", "Kenya"}

print("Nigeria" in countries)

Output:

True

You can also use not in.

print("Canada" not in countries)

Set Union

One of the most useful features of sets is the ability to combine collections while automatically removing duplicates.

This operation is called a union.

set_a = {"apple", "banana", "orange"}

set_b = {"orange", "mango", "pawpaw"}

result = set_a.union(set_b)

print(result)

The result contains values from both sets.

The duplicate "orange" appears only once.

You can also use the | operator:

result = set_a | set_b

Set Intersection

Intersection finds the values that two sets have in common.

set_a = {"apple", "banana", "orange"}

set_b = {"orange", "mango", "banana"}

result = set_a.intersection(set_b)

print(result)

The common values are:

{'banana', 'orange'}

You can also use the & operator:

result = set_a & set_b

Set Difference

Difference finds values that are present in one set but not in another.

set_a = {"apple", "banana", "orange"}

set_b = {"orange", "mango"}

result = set_a.difference(set_b)

print(result)

The result contains values that are in set_a but not in set_b.

You can also use the - operator:

result = set_a - set_b

Symmetric Difference

Symmetric difference returns values that belong to either set, but not values that belong to both.

set_a = {"apple", "banana", "orange"}

set_b = {"orange", "mango"}

result = set_a.symmetric_difference(set_b)

print(result)

orange is excluded because it appears in both sets.

The ^ operator can also be used:

result = set_a ^ set_b

Checking for a Subset

A set is a subset of another set when every item in the first set is also contained in the second set.

small_set = {1, 2}

large_set = {1, 2, 3, 4}

print(small_set.issubset(large_set))

Output:

True

You can also use the <= operator.

Checking for a Superset

A set is a superset when it contains every item from another set.

small_set = {1, 2}

large_set = {1, 2, 3, 4}

print(large_set.issuperset(small_set))

Output:

True

Checking for Disjoint Sets

Two sets are disjoint when they have no items in common.

set_a = {1, 2, 3}

set_b = {4, 5, 6}

print(set_a.isdisjoint(set_b))

Output:

True

Looping Through a Set

You can loop through a set using a for loop.

fruits = {"apple", "banana", "orange"}

for fruit in fruits:
    print(fruit)

Remember that you should not depend on the order in which the items are printed.

Converting Other Collections into Sets

The set() function can convert another iterable into a set.

Convert a List

numbers = [1, 2, 2, 3, 3, 4]

unique_numbers = set(numbers)

print(unique_numbers)

Convert a Tuple

numbers = (1, 2, 2, 3, 3, 4)

unique_numbers = set(numbers)

print(unique_numbers)

This is a convenient way to remove duplicate values.

What Is a frozenset?

Python also provides a collection called a frozenset.

A frozenset behaves like a set but cannot be modified after it has been created.

numbers = frozenset([1, 2, 3, 4])

print(numbers)

You cannot use methods such as add() or remove() to change a frozenset.

You do not need to master frozensets yet. Just remember that they are an immutable version of a set.

Real-World Examples of Sets

1. Removing Duplicate Names

names = [
    "Ada",
    "John",
    "Ada",
    "Mary",
    "John"
]

unique_names = set(names)

print(unique_names)

2. Registered Courses

morning_students = {"Ada", "John", "Mary"}

afternoon_students = {"Mary", "David", "Sarah"}

all_students = morning_students | afternoon_students

print(all_students)

The union operation gives us everyone who attended either session without duplicates.

3. Finding Common Interests

python_students = {"Ada", "John", "Mary"}

robotics_students = {"Mary", "David", "John"}

both = python_students & robotics_students

print(both)

The intersection gives students who belong to both groups.

4. Agriculture Example

Imagine collecting crop types from several farms.

farm_a = {"maize", "rice", "cassava"}

farm_b = {"rice", "yam", "cassava"}

common_crops = farm_a & farm_b

print(common_crops)

The intersection shows crops that both farms grow.

Common Beginner Mistakes

1. Trying to access a set using an index

fruits = {"apple", "banana", "orange"}

print(fruits[0])

Sets do not support indexing.

2. Expecting a set to preserve order

Do not build your program around the assumption that a set will display items in the same order you entered them.

3. Using {} when you want an empty set

Remember:

empty = {}

creates a dictionary.

Use:

empty = set()

4. Expecting duplicates

numbers = {1, 1, 2, 2, 3}

print(numbers)

Duplicate values are automatically removed.

5. Confusing remove() and discard()

remove() raises an error if the item does not exist, while discard() does not.

Practice Exercises

Exercise 1 — Create a Set

Create a set containing five fruits.

Exercise 2 — Duplicates

Create a set containing repeated numbers and observe what happens to the duplicates.

Exercise 3 — Membership

Use in to check whether a particular value exists in your set.

Exercise 4 — Add

Add two new values using add().

Exercise 5 — Remove

Remove an item using remove().

Exercise 6 — Union

Create two sets of fruits and combine them using union.

Exercise 7 — Intersection

Create two sets of students and find the students who appear in both sets.

Exercise 8 — Difference

Find the values that appear in one set but not the other.

Exercise 9 — Remove Duplicates

Create a list containing duplicate values and convert it into a set.

Mini Project: Unique Student Names

Let's build a small program that removes duplicate student names.

students = [
    "Ada",
    "John",
    "Mary",
    "Ada",
    "David",
    "John",
    "Mary"
]

unique_students = set(students)

print("Original names:")
print(students)

print("Unique names:")
print(unique_students)

This demonstrates one of the most practical uses of sets: removing duplicates from a collection.

Try extending the program by allowing the user to enter several names and then displaying only the unique names.

Python Sets Quiz

Test your understanding before moving on.

1. What is one major feature of a set?

2. Which brackets are commonly used for a set?

3. How do you create an empty set?

4. Can you access set items using indexes?

5. Which method adds one item to a set?

6. Which operation finds values common to two sets?

7. What does set() commonly help you do with a list?

8. What is the difference between remove() and discard()?

Python Sets Summary

Sets are collections designed primarily for storing unique values and performing mathematical-style set operations.

Concept Purpose
Set Stores unique values.
{} Common syntax for a non-empty set.
set() Creates an empty set or converts an iterable.
add() Adds one item.
update() Adds multiple items.
remove() Removes an item and raises an error if missing.
discard() Removes an item without raising an error if missing.
clear() Removes all items.
union() Combines values from two sets.
intersection() Finds values shared by two sets.
difference() Finds values in one set but not another.
symmetric_difference() Finds values that are in either set but not both.
issubset() Checks whether one set is contained in another.
issuperset() Checks whether one set contains another.
isdisjoint() Checks whether two sets have no values in common.
Key idea:

Use a set when uniqueness matters or when you need to compare collections using operations such as union, intersection and difference.

```html

Python Dictionaries

So far, you have learned how Python stores collections of data using lists, tuples and sets.

Now we are going to learn one of the most useful and important Python data structures: the dictionary.

Dictionaries allow you to store information using key-value pairs.

Simple idea: A dictionary stores information using a label and the value associated with that label.

Think about a real dictionary. You look up a word and find its meaning. Python dictionaries work in a similar way: you use a key to find its associated value.

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

Here:

  • "name" is a key.
  • "Ada" is its value.
  • "age" is a key.
  • 21 is its value.
  • "score" is a key.
  • 85 is its value.

What Is a Dictionary?

A dictionary is a collection of data stored as key-value pairs.

Dictionaries are written using curly braces:

person = {
    "name": "Olivia",
    "age": 27,
    "country": "Nigeria"
}

Each key is separated from its value using a colon :.

The general structure looks like this:

{
    key: value
}

Multiple key-value pairs are separated by commas.

Understanding Key-Value Pairs

The key identifies the information, while the value contains the information itself.

student = {
    "name": "Ada",
    "age": 21,
    "course": "Biology"
}

You can think about this as:

Key Value
name Ada
age 21
course Biology

The key gives you a way to find the corresponding value.

Creating a Dictionary

You create a dictionary using curly braces {}.

car = {
    "brand": "Toyota",
    "model": "Corolla",
    "year": 2025
}

You can also create an empty dictionary:

person = {}

Values can be strings, numbers, Boolean values, lists, dictionaries and other Python objects.

student = {
    "name": "Ada",
    "age": 21,
    "passed": True,
    "scores": [80, 85, 90]
}

Accessing Dictionary Values

To access a value, use its key inside square brackets.

student = {
    "name": "Ada",
    "age": 21
}

print(student["name"])

Output:

Ada

You can access the age in the same way:

print(student["age"])

Output:

21
Remember: Lists use numeric indexes such as 0 and 1. Dictionaries use keys such as "name" and "age".

Using get()

You can also access dictionary values using the get() method.

student = {
    "name": "Ada",
    "age": 21
}

print(student.get("name"))

Output:

Ada

One useful difference is what happens when the key does not exist.

print(student.get("email"))

This returns:

None

You can also provide a default value:

print(student.get("email", "Not provided"))

Output:

Not provided

Changing Dictionary Values

Dictionaries are changeable. You can modify the value associated with an existing key.

student = {
    "name": "Ada",
    "score": 75
}

student["score"] = 90

print(student)

The score has been changed from 75 to 90.

Adding New Items

To add a new key-value pair, simply assign a value to a new key.

student = {
    "name": "Ada",
    "age": 21
}

student["course"] = "Biology"

print(student)

The new key "course" has been added.

Removing Items with pop()

The pop() method removes an item using its key.

student = {
    "name": "Ada",
    "age": 21,
    "course": "Biology"
}

student.pop("age")

print(student)

The "age" key and its value are removed.

Removing the Last Inserted Item

The popitem() method removes and returns the last inserted key-value pair.

student = {
    "name": "Ada",
    "age": 21,
    "course": "Biology"
}

removed = student.popitem()

print(removed)
print(student)

This is useful when you specifically want to remove the last inserted pair.

Deleting Dictionary Items with del

You can use del to remove an item by its key.

student = {
    "name": "Ada",
    "age": 21
}

del student["age"]

print(student)

You can also delete the entire dictionary.

del student

Removing Everything with clear()

The clear() method removes all items from the dictionary.

student = {
    "name": "Ada",
    "age": 21
}

student.clear()

print(student)

Output:

{}

Finding the Length of a Dictionary

Use len() to find the number of key-value pairs.

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

print(len(student))

Output:

3

Getting Dictionary Keys

The keys() method returns a view containing the dictionary's keys.

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

print(student.keys())

You can loop through the keys:

for key in student.keys():
    print(key)

Getting Dictionary Values

The values() method returns the dictionary's values.

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

for value in student.values():
    print(value)

Getting Keys and Values with items()

The items() method allows you to work with keys and values together.

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

for key, value in student.items():
    print(key, ":", value)

Output:

name : Ada
age : 21
score : 85

This pattern is extremely useful when processing dictionary data.

Checking Whether a Key Exists

Use the in operator to check whether a key exists.

student = {
    "name": "Ada",
    "age": 21
}

print("name" in student)

Output:

True

You can also use not in.

print("email" not in student)

Updating a Dictionary

The update() method can add new key-value pairs or change existing ones.

student = {
    "name": "Ada",
    "score": 75
}

student.update({
    "score": 90,
    "course": "Biology"
})

print(student)

The existing score was updated and the new course was added.

Copying a Dictionary

You can use copy() to create a copy of a dictionary.

student = {
    "name": "Ada",
    "score": 85
}

student_copy = student.copy()

print(student_copy)

This creates a separate dictionary object containing the same key-value pairs.

Nested Dictionaries

A dictionary can contain another dictionary.

students = {
    "student1": {
        "name": "Ada",
        "score": 85
    },

    "student2": {
        "name": "John",
        "score": 78
    }
}

To access Ada's score:

print(students["student1"]["score"])

Output:

85

Nested dictionaries are useful when working with structured information.

Lists Inside Dictionaries

A dictionary can also contain lists.

student = {
    "name": "Ada",
    "scores": [80, 85, 90]
}

You can access the list:

print(student["scores"])

And access an individual score:

print(student["scores"][0])

Output:

80

Dictionaries with Conditions

Dictionaries become even more useful when combined with conditions.

student = {
    "name": "Ada",
    "score": 75
}

if student["score"] >= 50:
    print("Pass")
else:
    print("Fail")

Python gets the student's score from the dictionary and then evaluates the condition.

Looping Through a Dictionary

You can loop through a dictionary in several ways.

Loop Through Keys

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

for key in student:
    print(key)

Loop Through Values

for value in student.values():
    print(value)

Loop Through Both

for key, value in student.items():
    print(key, value)

Dictionary Comprehension

Python provides a compact way to create dictionaries called dictionary comprehension.

For example:

squares = {
    number: number * number
    for number in range(1, 6)
}

print(squares)

Output:

{1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

If this syntax looks unfamiliar, do not worry. Learn ordinary dictionaries and loops first.

Dictionary Key Rules

Dictionary keys need to be hashable. Common examples include strings, integers and tuples containing suitable values.

data = {
    "name": "Ada",
    1: "One",
    (2, 3): "Coordinates"
}

Lists and other mutable objects cannot be used directly as dictionary keys.

For beginners, strings are usually the easiest and clearest choice for dictionary keys.

Real-World Examples of Dictionaries

1. Student Information

student = {
    "name": "Ada",
    "age": 21,
    "department": "Biology",
    "score": 88
}

2. Product Information

product = {
    "name": "Laptop",
    "price": 850000,
    "stock": 12
}

3. Weather Data

weather = {
    "temperature": 29,
    "humidity": 78,
    "condition": "Cloudy"
}

4. Robotics Data

robot = {
    "name": "AgriBot",
    "battery": 87,
    "speed": 1.5,
    "active": True
}

A robotics program could use a dictionary to keep related state information together.

5. Agricultural Data

crop = {
    "name": "Maize",
    "soil_moisture": 42,
    "temperature": 28,
    "healthy": True
}

This is a simple example of how structured information about a crop could be represented in Python.

Common Beginner Mistakes

1. Forgetting the colon

Every key-value pair needs a colon.

student = {
    "name": "Ada",
    "age": 21
}

2. Trying to use a list index

Dictionaries are accessed using keys.

student["name"]

3. Accessing a key that does not exist

student["email"]

If the key does not exist, this raises a KeyError.

If you want a safer lookup, consider:

student.get("email")

4. Confusing keys and values

student = {
    "name": "Ada"
}

"name" is the key. "Ada" is the value.

5. Accidentally replacing a value

student["score"] = 90

If "score" already exists, this changes its value. If it does not exist, it creates a new key.

Practice Exercises

Exercise 1 — Create a Dictionary

Create a dictionary containing your name, age, country and favorite programming language.

Exercise 2 — Access Values

Print each value using its key.

Exercise 3 — Add a Key

Add a new key called "occupation".

Exercise 4 — Change a Value

Change one of the existing values.

Exercise 5 — Remove an Item

Remove one item using pop().

Exercise 6 — Check a Key

Use in to check whether a particular key exists.

Exercise 7 — Loop Through a Dictionary

Use items() to print every key and value.

Exercise 8 — Student Record

Create a dictionary containing a student's name and five scores stored inside a list.

Exercise 9 — Agricultural Data

Create a dictionary describing a crop. Include the crop name, temperature, soil moisture and whether the crop is healthy.

Mini Project: Student Record

Let's create a small student record using a dictionary.

student = {
    "name": "Ada",
    "age": 21,
    "scores": [78, 85, 91]
}

print("Name:", student["name"])
print("Age:", student["age"])

scores = student["scores"]

print("Scores:", scores)
print("Highest Score:", max(scores))
print("Lowest Score:", min(scores))
print("Total Score:", sum(scores))
print("Average Score:", sum(scores) / len(scores))

Notice how the dictionary stores the student's information, while the list stores multiple scores.

This is an important programming idea: different data structures can work together.

Python Dictionaries Quiz

Test your understanding before moving on.

1. What does a Python dictionary store?

2. Which brackets are commonly used for dictionaries?

3. Which symbol separates a key from its value?

4. How do you access the value associated with the key "name"?

student = {"name": "Ada"}

5. Which method can safely return None when a key does not exist?

6. Which method returns dictionary keys?

7. Which method allows you to loop through keys and values together?

8. What happens when you assign a new value to an existing dictionary key?

Python Dictionaries Summary

Dictionaries organize information using key-value pairs. They are particularly useful when you need to give meaningful names to pieces of data.

Concept Purpose
Dictionary Stores data as key-value pairs.
Key Identifies a value.
Value The data associated with a key.
get() Retrieves a value safely.
keys() Returns the dictionary's keys.
values() Returns the dictionary's values.
items() Provides key-value pairs for iteration.
update() Adds or changes key-value pairs.
pop() Removes an item by key.
popitem() Removes the last inserted key-value pair.
clear() Removes all items.
in Checks whether a key exists.
Key idea:

Use a dictionary when you want to associate meaningful keys with values. For example, instead of remembering that index 0 means a student's name, you can simply use student["name"].

```html

Python Functions

Imagine that you are building a large Python program. As your program grows, you may find yourself writing the same instructions again and again.

This is where functions become extremely useful.

A function allows you to group a set of instructions together, give that group a name, and run it whenever you need it.

Simple idea: A function is a reusable block of code designed to perform a particular task.
def greet():
    print("Hello!")

greet()

Instead of writing the print() instruction every time, we placed it inside a function called greet().

Whenever we want the function to run, we simply call:

greet()

What Is a Function?

A function is a named block of code that performs a specific task.

Think of a function like a machine.

You give the machine some information, the machine performs an operation, and sometimes it gives you a result.

Input → Function → Output

For example, imagine a function that calculates the area of a rectangle.

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

You can then use it:

area = rectangle_area(10, 5)

print(area)

Output:

50

Why Use Functions?

Functions help you organize your programs and avoid unnecessary repetition.

1. Reuse Code

Write the instructions once and use them multiple times.

2. Make Programs Easier to Understand

A well-named function can tell you what a section of code does.

3. Reduce Repetition

Instead of copying the same code throughout your program, place it inside a function.

4. Make Debugging Easier

When something goes wrong, smaller sections of code are generally easier to inspect.

5. Build Larger Programs

Real applications are often divided into smaller tasks. Functions help you build those tasks separately.

Defining a Function

You create a function using the def keyword.

def greet():
    print("Hello!")

Let's break this down.

  • def tells Python that you are defining a function.
  • greet is the function's name.
  • () contains parameters, if the function has any.
  • : marks the beginning of the function body.
  • The indented code belongs to the function.

Calling a Function

Defining a function does not automatically execute it.

def greet():
    print("Hello!")

Nothing is printed yet.

To execute the function, you call it:

greet()

Output:

Hello!

You can call the same function multiple times.

greet()
greet()
greet()

Output:

Hello!
Hello!
Hello!

Function Parameters

A parameter allows a function to receive information.

def greet(name):
    print("Hello", name)

Here, name is a parameter.

When calling the function, provide an argument:

greet("Ada")

Output:

Hello Ada

You can call it with different values:

greet("John")
greet("Mary")
greet("David")

The same function can therefore work with different data.

Parameters vs Arguments

These two terms are related but are not exactly the same.

def greet(name):
    print("Hello", name)

name is the parameter.

greet("Ada")

"Ada" is the argument.

Easy way to remember: The parameter is the placeholder. The argument is the actual value you give the function.

Multiple Parameters

A function can have more than one parameter.

def add_numbers(a, b):
    print(a + b)

Call the function:

add_numbers(10, 5)

Output:

15

The first argument goes into a, and the second goes into b.

The return Statement

A function can calculate something and send the result back using return.

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

You can store the returned value:

result = add(10, 5)

print(result)

Output:

15

This is different from simply printing the result.

print() displays something. return sends a value back to the place where the function was called.

Default Parameters

You can give a parameter a default value.

def greet(name="Guest"):
    print("Hello", name)

If you provide a name:

greet("Ada")

Output:

Hello Ada

If you do not provide one:

greet()

Output:

Hello Guest

Keyword Arguments

You can specify arguments using the parameter names.

def introduce(name, age):
    print(name, age)

introduce(age=21, name="Ada")

Notice that the arguments were provided in a different order, but Python knows which value belongs to which parameter.

Positional Arguments

When you provide arguments without naming them, their position determines which parameter receives each value.

def introduce(name, age):
    print(name, age)

introduce("Ada", 21)

"Ada" goes to name and 21 goes to age.

Using *args

Sometimes you do not know in advance how many positional arguments a function will receive.

Python allows you to use *args.

def add_numbers(*numbers):
    total = 0

    for number in numbers:
        total += number

    return total

print(add_numbers(2, 4, 6))
print(add_numbers(1, 2, 3, 4, 5))

The parameter numbers receives the positional arguments as a tuple.

Beginner note: You do not need *args for basic Python programs. Learn normal parameters first.

Using **kwargs

**kwargs allows a function to accept a variable number of keyword arguments.

def show_info(**details):

    for key, value in details.items():
        print(key, ":", value)

show_info(
    name="Ada",
    age=21,
    course="Biology"
)

The collected keyword arguments are stored in a dictionary.

This is a more advanced feature, so focus on ordinary parameters first.

Variable Scope

Variables created inside a function normally belong to that function.

def calculate():
    number = 10
    print(number)

calculate()

The variable number is local to the function.

You generally cannot access it directly outside the function.

def calculate():
    number = 10

calculate()

print(number)

This produces an error because number was created inside the function.

Global Variables and Functions

A variable created outside a function is generally available to code inside that function.

name = "Ada"

def greet():
    print("Hello", name)

greet()

Output:

Hello Ada

However, beginners should avoid relying heavily on global variables. Passing information into functions through parameters is usually clearer.

Functions with Conditions

Functions can contain if, elif and else statements.

def check_age(age):

    if age >= 18:
        return "Adult"
    else:
        return "Minor"

print(check_age(21))
print(check_age(15))

Output:

Adult
Minor

Functions with Loops

Functions can also contain loops.

def count_numbers():

    for number in range(1, 6):
        print(number)

count_numbers()

This combines two important Python concepts: functions and loops.

Functions with Lists

You can pass a list into a function.

def calculate_total(numbers):

    total = 0

    for number in numbers:
        total += number

    return total

scores = [70, 80, 90]

print(calculate_total(scores))

Output:

240

Functions with Dictionaries

Functions can also receive dictionaries.

def show_student(student):

    print("Name:", student["name"])
    print("Score:", student["score"])

student = {
    "name": "Ada",
    "score": 85
}

show_student(student)

This is useful when working with structured information.

Returning Multiple Values

Python allows a function to return multiple values.

def calculate(a, b):

    total = a + b
    difference = a - b

    return total, difference

You can store the returned values separately:

total, difference = calculate(10, 4)

print(total)
print(difference)

Output:

14
6

Python packages these returned values together, and you can unpack them into separate variables.

What Happens When a Function Has No return?

If a function does not explicitly return a value, Python returns None.

def greet():
    print("Hello")

result = greet()

print(result)

Output:

Hello
None

The function printed Hello, but it did not return a value.

Function Documentation

You can place a description inside a function using a docstring.

def add(a, b):
    """Return the sum of two numbers."""
    return a + b

A docstring helps explain what the function is designed to do.

Introduction to Recursion

Recursion happens when a function calls itself.

A simple example:

def countdown(number):

    if number <= 0:
        print("Done!")
        return

    print(number)

    countdown(number - 1)

countdown(5)

Output:

5
4
3
2
1
Done!

Recursion is an advanced concept. You do not need to master it before continuing with the rest of this beginner course.

Introduction to Lambda Functions

Python also provides a short way to create simple anonymous functions using lambda.

square = lambda number: number * number

print(square(5))

Output:

25

For now, think of lambda functions as a compact form for simple operations.

Don't rush this topic. Regular functions using def are more important for beginners and should be your primary focus.

Functions in Real-World Programs

Functions are everywhere in real software.

Student Management

def calculate_average(scores):
    return sum(scores) / len(scores)

Currency Conversion

def convert_currency(amount, rate):
    return amount * rate

Agriculture

def check_soil_moisture(moisture):

    if moisture < 30:
        return "Water needed"

    return "Moisture level okay"

Robotics

def calculate_distance(speed, time):
    return speed * time

In a robotics program, you might have separate functions for reading sensors, calculating movement, controlling motors, checking battery levels and processing camera information.

Breaking a large program into functions makes the system much easier to understand and maintain.

Writing Good Functions

A function should ideally have a clear responsibility.

Compare these two ideas:

def process_everything():
    ...

with:

def calculate_average(scores):
    ...

def check_pass_mark(score):
    ...

def display_result(name, result):
    ...

The second approach makes each task easier to understand.

Good rule: Give a function one clear job whenever practical.

Common Beginner Mistakes

1. Forgetting to call the function

def greet():
    print("Hello!")

Defining the function does not run it.

greet()

2. Forgetting the colon

def greet()
    print("Hello")

The function definition needs a colon.

3. Incorrect indentation

def greet():
print("Hello")

The function body must be indented.

4. Providing the wrong number of arguments

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

add(5)

The function requires two arguments.

5. Confusing print and return

Remember that displaying a result and returning a result are different operations.

6. Returning too early

def example():
    return 10
    print("This will not run")

Once Python executes return, the function ends.

7. Creating unnecessarily large functions

If a function becomes difficult to understand, consider breaking it into smaller functions.

Practice Exercises

Exercise 1 — Greeting Function

Create a function called greet() that prints "Hello, Python!".

Exercise 2 — Personalized Greeting

Create a function that accepts a person's name and prints a greeting.

Exercise 3 — Addition

Create a function that accepts two numbers and returns their sum.

Exercise 4 — Average

Create a function that accepts a list of numbers and returns the average.

Exercise 5 — Even or Odd

Create a function that accepts a number and returns "Even" if it is even and "Odd" otherwise.

Exercise 6 — Maximum Number

Create a function that accepts a list and returns its largest value.

Exercise 7 — Student Result

Create a function that accepts a student's score and returns "Pass" if the score is at least 50 and "Fail" otherwise.

Exercise 8 — Temperature Conversion

Create a function that converts Celsius to Fahrenheit.

Formula:

Fahrenheit = (Celsius * 9 / 5) + 32

Mini Project: Student Performance Analyzer

Let's combine functions, lists, conditions and basic calculations.

def calculate_total(scores):
    return sum(scores)


def calculate_average(scores):
    return sum(scores) / len(scores)


def check_result(average):

    if average >= 50:
        return "Pass"

    return "Fail"


scores = [65, 72, 81, 55]

total = calculate_total(scores)
average = calculate_average(scores)
result = check_result(average)

print("Scores:", scores)
print("Total:", total)
print("Average:", average)
print("Result:", result)

Notice how each function has one clear responsibility.

  • calculate_total() calculates the total.
  • calculate_average() calculates the average.
  • check_result() determines the result.

This is one of the main reasons programmers use functions: a complicated task can be broken into smaller, understandable pieces.

Python Functions Quiz

Test what you have learned.

1. Which keyword is used to define a function?

2. What do you use to execute a function?

3. What does a parameter provide?

4. Which keyword sends a value back from a function?

5. What happens when a function has no explicit return value?

6. What is the difference between print() and return?

7. What does *args collect?

8. Why are functions useful?

Python Functions Summary

Concept Purpose
def Defines a function.
Function call Executes a function.
Parameter Receives information inside a function.
Argument The actual value supplied to a parameter.
return Sends a value back from a function.
Default parameter Provides a value when an argument is omitted.
Keyword argument Passes a value using its parameter name.
*args Collects variable positional arguments.
**kwargs Collects variable keyword arguments.
Local variable A variable created inside a function.
Docstring Documents what a function does.
Key idea:

Functions allow you to break a large program into smaller, reusable pieces. Instead of writing one enormous block of code, you can create functions that each perform a clear task.

As your Python programs become more advanced, functions will become one of the tools you use most frequently.

```html id="q7m4kc"

Python Modules

As your Python programs become larger, putting everything into one file can quickly become difficult to manage.

Imagine building a large agricultural application that contains hundreds or thousands of lines of code. You might have code for calculating data, working with dates, reading files, processing images, communicating with sensors and controlling equipment.

Putting all of that into one file would make the program harder to understand and maintain.

Python solves this problem by allowing you to organize related code into separate files called modules.

Simple idea: A module is a Python file containing code that you can reuse in another Python program.
math_tools.py

If math_tools.py contains useful functions, another Python file can import them and use them.


What Is a Module?

A module is simply a Python file with a .py extension.

For example:

calculator.py
weather.py
students.py
robot.py
crop_data.py

Each file can contain variables, functions, classes and other Python code.

You can then import that code into another Python program.

Why Use Modules?

Modules help you organize your programs and reuse code.

1. Organize Your Code

Related functionality can be placed in its own file.

2. Reuse Code

You can write a function once and use it in multiple programs.

3. Make Programs Easier to Maintain

Smaller files are generally easier to understand than one extremely large file.

4. Avoid Repetition

Instead of copying the same functions into different programs, place them inside a module and import them.

5. Work on Larger Projects

Modules are one of the building blocks used to structure larger Python applications.

Creating Your Own Module

Let's create a simple module.

Create a file called:

math_tools.py

Put the following code inside it:

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


def subtract(a, b):
    return a - b

You have now created your own Python module.

Importing a Module

To use a module, you can use the import keyword.

Create another file in the same folder:

main.py

Then write:

import math_tools

You can now use functions from the module.

import math_tools

result = math_tools.add(10, 5)

print(result)

Output:

15

The dot:

math_tools.add

means that you are accessing add from the math_tools module.

Importing Multiple Modules

A program can import more than one module.

import math_tools
import random

You can then use functionality from both modules.

Importing Specific Functions

Instead of importing the entire module name, you can import a specific function.

from math_tools import add

print(add(10, 5))

Now you can call add() directly.

Compare: import math_tools requires math_tools.add(), while from math_tools import add allows you to use add() directly.

Importing Multiple Items

You can import more than one function from a module.

from math_tools import add, subtract

print(add(10, 5))
print(subtract(10, 5))

Output:

15
5

Variables Inside Modules

Modules can contain variables as well as functions.

Suppose student_data.py contains:

school = "Gabbywall Academy"
students = 120

Another program can import the module:

import student_data

print(student_data.school)
print(student_data.students)

Python's Built-In Modules

Python comes with many modules that provide useful functionality.

You do not have to create everything yourself.

Some useful examples include:

  • math — mathematical functions.
  • random — random numbers and selections.
  • datetime — dates and times.
  • os — operating-system related functionality.
  • json — working with JSON data.
  • statistics — statistical calculations.

The math Module

The math module provides additional mathematical functions.

import math

print(math.sqrt(25))

Output:

5.0

Another example:

import math

print(math.pi)

The module provides many other mathematical tools.

The random Module

The random module is useful when you need random values or random selections.

import random

number = random.randint(1, 10)

print(number)

The program produces a random integer between 1 and 10, inclusive.

This module can be useful when creating games, simulations, testing programs and other applications.

The datetime Module

Python's datetime module provides tools for working with dates and times.

from datetime import datetime

now = datetime.now()

print(now)

This retrieves the current date and time from the computer's environment.

Using an Alias

You can give an imported module a shorter name using as.

import math as m

print(m.sqrt(25))

Here, m is an alias for math.

Aliases can be useful when a module name is long or when a commonly used abbreviation is appropriate.

Using dir()

The dir() function can help you inspect the names available inside a module or object.

import math

print(dir(math))

Python displays a list of names available in the module.

You do not need to memorize everything that appears. The purpose is to help you explore what is available.

Understanding if __name__ == "__main__"

You will eventually encounter code like this:

if __name__ == "__main__":
    print("Program started")

This is commonly used to make code run when a file is executed directly, but not automatically run when the file is imported as a module.

For example:

def greet():
    print("Hello")


if __name__ == "__main__":
    greet()

When this file is run directly, greet() executes.

When the file is imported into another program, the code inside the if block does not execute automatically.

Beginner note: You do not need to memorize this immediately. Understand the basic idea first: Python can tell whether a file is being run directly or imported.

Introduction to Packages

As projects become larger, you may need more than a few modules.

Python allows related modules to be organized into packages.

A simple way to think about it is:

Package
│
├── module1.py
├── module2.py
└── module3.py

A package is therefore a way of organizing related Python modules.

Packages become especially important when working with larger applications and external libraries.

Python's Standard Library

Python comes with a large collection of modules that are commonly referred to as the Python Standard Library.

This means that many useful capabilities are already available without you having to install additional packages.

For example:

import math
import random
import datetime
import json
import statistics

Learning how to discover and use these modules will make you much more productive as a Python programmer.

Modules in Real-World Projects

Imagine you are building an agricultural monitoring system.

Instead of placing everything in one file, you could organize your program like this:

agri_system/
│
├── main.py
├── sensors.py
├── soil.py
├── crops.py
├── weather.py
└── reports.py

Each module could have a different responsibility.

  • sensors.py could handle sensor readings.
  • soil.py could process soil information.
  • crops.py could store crop-related functions.
  • weather.py could process weather information.
  • reports.py could generate reports.
  • main.py could coordinate the application.

This approach keeps the project organized as it grows.

Robotics Example

robot_project/
│
├── main.py
├── motors.py
├── sensors.py
├── navigation.py
├── battery.py
└── camera.py

This type of organization becomes particularly useful when a project starts combining hardware, data processing and decision-making.

Common Beginner Mistakes

1. Misspelling the Module Name

import math_tools

The filename must be correctly named and Python must be able to find it.

2. Putting Files in the Wrong Location

When learning basic modules, keeping your Python files in the same project folder makes things easier.

3. Forgetting the Dot

import math_tools

print(math_tools.add(2, 3))

If you imported the whole module, remember to access its function through the module name.

4. Confusing Module and Function Names

A module and the functions inside that module are different things.

math_tools.add()

Here math_tools is the module and add() is the function.

5. Naming Your File After a Standard Module

Avoid naming your own file something like math.py, random.py or json.py.

Such names can cause confusing import problems because Python may find your file instead of the standard library module you intended to use.

6. Importing Everything Without Understanding It

When learning, understand what you are importing and why you need it.

Practice Exercises

Exercise 1 — Create a Module

Create a file called calculator.py.

Add functions for addition and subtraction.

Exercise 2 — Import the Module

Create main.py and import your calculator module.

Exercise 3 — Import Specific Functions

Import only the addition function from your module.

Exercise 4 — Create a Student Module

Create a module containing a function that calculates the average of a list of scores.

Exercise 5 — Use the math Module

Import math and use sqrt() to find the square root of 144.

Exercise 6 — Use the random Module

Generate a random number between 1 and 100.

Exercise 7 — Create an Agriculture Module

Create a module called crop_tools.py containing a function that checks soil moisture.

The function should return "Water needed" when moisture is below 30 and "Moisture okay" otherwise.

Mini Project: Modular Student System

Let's create a small project using multiple Python files.

File 1 — student_tools.py

def calculate_total(scores):
    return sum(scores)


def calculate_average(scores):
    return sum(scores) / len(scores)


def check_result(average):

    if average >= 50:
        return "Pass"

    return "Fail"

File 2 — main.py

import student_tools

scores = [70, 80, 65, 90]

total = student_tools.calculate_total(scores)
average = student_tools.calculate_average(scores)
result = student_tools.check_result(average)

print("Scores:", scores)
print("Total:", total)
print("Average:", average)
print("Result:", result)

The important idea is not the size of this project. The important idea is that we separated reusable functions from the main program.

As your projects become larger, this separation becomes much more valuable.

Python Modules Quiz

1. What is a Python module?

2. Which keyword is commonly used to import a module?

3. If you write import math_tools, how could you call the add function?

4. Which statement imports a specific function?

5. Which module is commonly used for random values?

6. Which module provides mathematical functions such as sqrt()?

7. What is one major benefit of modules?

8. What does the dot in math.sqrt() represent?

Python Modules Summary

Concept Purpose
Module A Python file containing reusable code.
import Imports a module.
from ... import ... Imports specific items from a module.
as Creates an alias for an imported module or item.
dir() Helps inspect names available in a module or object.
Package Organizes related Python modules.
Standard Library Collection of modules included with Python.
Key idea:

Modules allow you to divide a Python program into organized, reusable pieces. Instead of putting everything into one enormous file, you can create separate modules for different responsibilities.

You have now moved from writing individual functions to organizing functions and other code into reusable files.

```html

Python File Handling

So far, most of the Python programs you have written have worked with data temporarily stored in variables, lists, tuples, dictionaries and other objects.

But what happens when you want your program to save information so that it is still available after the program closes?

This is where file handling becomes important.

Simple idea: File handling allows a Python program to create, read, write, modify and delete files stored on a computer.

For example, a student management program could save student records to a file. An agricultural application could save sensor readings. A robotics program could store logs from a robot.

Python Program
      ↓
    File
      ↓
Saved Information

What Is File Handling?

File handling means using Python to work with files on a computer.

A file might contain:

  • Text
  • Numbers
  • Student records
  • Configuration information
  • Logs
  • Sensor readings
  • Reports

Python provides built-in tools that allow you to interact with these files.

Common File Types

You will encounter many different file types while programming.

Extension Common Use
.txt Plain text
.csv Tabular data
.json Structured data
.py Python source code
.log Program or system logs

In this lesson, we will mainly work with text files.

Opening a File

Python provides the open() function for opening files.

file = open("data.txt")

This tells Python to open a file called data.txt.

However, simply opening a file is not enough. You also need to decide what you want to do with it.

File Modes

The second argument of open() determines how the file will be used.

Mode Meaning
"r" Read the file
"w" Write to the file
"a" Append to the file
"x" Create a new file

For beginners, focus first on r, w and a.

Reading a File

Suppose notes.txt contains:

Python is interesting.
I am learning file handling.

You can read the file using:

file = open("notes.txt", "r")

content = file.read()

print(content)

file.close()

Output:

Python is interesting.
I am learning file handling.

Closing a File

After working with a file, you should close it when using the basic open() approach.

file = open("notes.txt", "r")

content = file.read()

print(content)

file.close()

Closing the file tells Python that you are finished working with it.

Important: In modern Python, the preferred approach is usually to use with open(...), which automatically handles closing the file.

Using with open()

A cleaner and safer way to work with files is to use the with statement.

with open("notes.txt", "r") as file:
    content = file.read()

print(content)

When the with block finishes, Python takes care of closing the file.

This is the style you should become comfortable using.

The read() Method

The read() method reads the contents of a file.

with open("notes.txt", "r") as file:
    content = file.read()

print(content)

You can also specify how many characters to read.

with open("notes.txt", "r") as file:
    content = file.read(10)

print(content)

This reads up to 10 characters.

The readline() Method

The readline() method reads one line at a time.

with open("notes.txt", "r") as file:

    first_line = file.readline()

    print(first_line)

You can call it again to read the next line.

with open("notes.txt", "r") as file:

    print(file.readline())
    print(file.readline())

The readlines() Method

The readlines() method reads the lines and returns them as a list.

with open("notes.txt", "r") as file:
    lines = file.readlines()

print(lines)

For example, the result might look like:

[
    "Python is interesting.\n",
    "I am learning file handling.\n"
]

Looping Through a File

You can loop through a file one line at a time.

with open("notes.txt", "r") as file:

    for line in file:
        print(line)

This approach is useful when working with files containing many lines of information.

Writing to a File

Use "w" when you want to write content to a file.

with open("notes.txt", "w") as file:
    file.write("Hello from Python!")

If the file does not exist, Python can create it.

If the file already contains information, writing with "w" replaces its existing contents.

Be careful with "w". It can overwrite existing file content.

Writing Multiple Lines

You can write multiple lines using newline characters.

with open("notes.txt", "w") as file:

    file.write("Python\n")
    file.write("JavaScript\n")
    file.write("C++\n")

The \n character moves the next text to a new line.

Appending to a File

Use "a" when you want to add information to the end of an existing file without replacing its contents.

with open("notes.txt", "a") as file:
    file.write("\nAnother line.")

This adds the new content after the existing content.

Creating a New File

The "x" mode can be used to create a new file.

with open("new_file.txt", "x") as file:
    file.write("This is a new file.")

If the file already exists, Python raises an error rather than replacing it.

Checking Whether a File Exists

The os module provides tools for interacting with the operating system.

import os

if os.path.exists("notes.txt"):
    print("File exists")
else:
    print("File does not exist")

This can help your program avoid trying to work with a file that is not available.

Deleting a File

Python can also delete files.

import os

if os.path.exists("notes.txt"):
    os.remove("notes.txt")
Be careful: File deletion can permanently remove information. Always make sure you are deleting the correct file.

Understanding File Paths

A file path tells Python where a file is located.

A simple filename:

"notes.txt"

refers to a file Python can find from the program's current working location.

You can also work with files inside folders.

"data/notes.txt"

When working with paths in larger programs, Python's pathlib module provides a modern and convenient way to handle paths.

Introduction to pathlib

Python provides the pathlib module for working with filesystem paths.

from pathlib import Path

file_path = Path("notes.txt")

if file_path.exists():
    print("The file exists")

You can also read a small text file using:

from pathlib import Path

content = Path("notes.txt").read_text()

print(content)

pathlib becomes particularly useful when working with files and folders across different operating systems.

File Encoding

Text files are stored using character encodings. UTF-8 is a common choice for text files.

You can explicitly specify it when opening a text file.

with open("notes.txt", "r", encoding="utf-8") as file:
    content = file.read()

print(content)

Being explicit about encoding can help your program correctly handle a wide range of characters and languages.

Common File Errors

File operations can fail for several reasons.

FileNotFoundError

This occurs when Python tries to open a file that cannot be found.

with open("missing.txt", "r") as file:
    content = file.read()

PermissionError

This can happen when your program does not have permission to perform the requested operation.

IsADirectoryError

This can occur when code expecting a file is given a directory instead.

You will learn how to handle these errors properly in the next major section on Errors and Exceptions.

Saving Dictionary Information

You can convert information into text and save it to a file.

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

with open("student.txt", "w") as file:

    file.write("Name: " + student["name"] + "\n")
    file.write("Age: " + str(student["age"]) + "\n")
    file.write("Score: " + str(student["score"]))

Notice that numbers are converted to strings before being combined with text.

File Handling in Real-World Programs

File handling becomes particularly useful when your program needs to preserve information between executions.

Student Management System

A program could save student records to a file.

Application Logs

with open("app.log", "a") as file:
    file.write("Program started\n")

Agricultural Monitoring

soil_moisture = 42

with open("sensor_log.txt", "a") as file:
    file.write("Soil moisture: " + str(soil_moisture) + "\n")

Robotics

battery = 87
speed = 1.5

with open("robot_log.txt", "a") as file:

    file.write(
        "Battery: " + str(battery) +
        "%, Speed: " + str(speed) +
        "\n"
    )

A real robot could generate thousands of sensor readings. Saving those readings allows you to inspect them later.

Introduction to CSV Files

CSV stands for Comma-Separated Values.

CSV files are commonly used to store tabular data.

Name,Age,Score
Ada,21,85
John,22,78
Mary,20,91

Python provides the built-in csv module for working with CSV files.

import csv

with open("students.csv", "r", newline="") as file:

    reader = csv.reader(file)

    for row in reader:
        print(row)

CSV files are especially useful when working with datasets and spreadsheet-style information.

Introduction to JSON Files

JSON is another common format for storing structured data.

{
    "name": "Ada",
    "age": 21,
    "score": 85
}

Python provides the json module for working with JSON data.

import json

student = {
    "name": "Ada",
    "age": 21,
    "score": 85
}

with open("student.json", "w") as file:
    json.dump(student, file, indent=4)

JSON becomes especially important when applications need to exchange structured data.

Common Beginner Mistakes

1. Forgetting the File Name

open("notes.txt", "r")

Make sure the filename and path are correct.

2. Using the Wrong Mode

Remember:

  • r reads.
  • w writes and can replace existing content.
  • a adds content to the end.
  • x creates a new file.

3. Accidentally Overwriting a File

with open("important.txt", "w") as file:
    file.write("New content")

Using w can replace existing content.

4. Forgetting to Convert Numbers to Strings

age = 21

with open("student.txt", "w") as file:
    file.write(age)

This does not work because write() expects text.

Convert the number:

file.write(str(age))

5. Not Handling Missing Files

A program should account for the possibility that a file does not exist.

6. Using Complicated Paths Without Understanding Them

When learning, start with simple project folders and gradually learn more advanced path handling.

Practice Exercises

Exercise 1 — Create a File

Create a file called hello.txt and write "Hello Python!" into it.

Exercise 2 — Read the File

Write a program that reads hello.txt and prints its contents.

Exercise 3 — Add Another Line

Use append mode to add another sentence without deleting the existing content.

Exercise 4 — Student File

Create a text file containing a student's name, age and score.

Exercise 5 — Count Lines

Create a program that opens a text file and counts how many lines it contains.

Exercise 6 — Sensor Log

Create a program that asks for a temperature and appends the reading to a file.

Exercise 7 — Check a File

Use os.path.exists() to check whether a file exists before trying to open it.

Exercise 8 — CSV Practice

Create a CSV file containing three students and their scores. Use Python to read and print each row.

Mini Project: Simple Notes App

Let's build a very small notes application.

The program will allow the user to type a note and save it to a file.

note = input("Enter your note: ")

with open("notes.txt", "a") as file:
    file.write(note + "\n")

print("Note saved successfully.")

Every time you run the program, the new note is added to the file instead of replacing the previous notes.

Reading the Notes

with open("notes.txt", "r") as file:

    for line in file:
        print(line.strip())

You now have the foundation of a simple persistent notes application.

The important concept is that the information survives after the Python program closes because it has been stored in a file.

Python File Handling Quiz

1. Which function is commonly used to open a file?

2. Which mode is used to read a file?

3. Which mode can overwrite existing file content?

4. Which mode adds content to the end of a file?

5. What does read() do?

6. Why is with open() useful?

7. Which module can be used to check whether a file exists?

8. What does CSV commonly represent?

Python File Handling Summary

Concept Purpose
open() Opens a file.
"r" Reads a file.
"w" Writes to a file and can replace its contents.
"a" Appends content to a file.
"x" Creates a new file.
read() Reads file content.
readline() Reads one line.
readlines() Reads lines into a list.
write() Writes text into a file.
os.path.exists() Checks whether a path exists.
os.remove() Removes a file.
pathlib Provides modern tools for working with paths.
csv Works with CSV data.
json Works with JSON data.
Key idea:

Variables hold data while your program is running. Files allow your program to store information so it can be used again later.

This is an important step toward building useful applications. Once your programs can save and retrieve information, they can start behaving more like real-world software.

```html

Python Errors & Exceptions

When you write Python programs, your code will not always work perfectly on the first attempt.

You may misspell something, use the wrong type of data, try to open a file that does not exist, or perform an operation that Python cannot complete.

Python responds to these problems by producing errors or exceptions.

Simple idea: An error tells you that something went wrong. Exception handling gives your program a way to respond to certain problems instead of stopping unexpectedly.

What Is an Error?

An error is a problem in your program that prevents Python from doing what you asked it to do.

For example:

print("Hello"

The closing parenthesis is missing, so Python cannot correctly understand the instruction.

Python will report the problem instead of pretending that everything is fine.

Why Do Errors Matter?

Learning programming does not mean learning how to write code without ever making mistakes.

In real programming, errors are normal.

The important skill is learning how to:

  • Recognize what went wrong.
  • Read the error message.
  • Find the part of the program causing the problem.
  • Fix the underlying issue.
  • Handle expected problems gracefully.

Error messages are therefore not your enemy. They are clues from Python.

Syntax Errors

A syntax error happens when Python cannot understand the structure of your code.

Think of syntax as the grammar of Python.

For example:

if age >= 18
    print("Adult")

The colon after the condition is missing.

The correct version is:

if age >= 18:
    print("Adult")
Remember: Syntax errors are usually problems with how the code is written, rather than what the program is trying to accomplish.

Indentation Errors

Python uses indentation to determine which statements belong to a block of code.

For example:

if age >= 18:
    print("You are an adult.")

If the indentation is missing or inconsistent, Python may raise an indentation-related error.

if age >= 18:
print("You are an adult.")

The print() statement needs to be indented.

NameError

A NameError commonly occurs when you try to use a variable or name that Python does not know.

name = "Olivia"

print(username)

Python knows about name, but not username.

A common cause is simply spelling a variable incorrectly.

student_name = "Ada"

print(student_nam)

The names are different, so Python cannot find the second one.

TypeError

A TypeError occurs when an operation is not valid for the type of data being used.

For example:

age = 20

print("Age: " + age)

You are trying to combine a string and an integer using string concatenation.

One solution is to convert the number to a string:

age = 20

print("Age: " + str(age))

Another common approach is an f-string:

age = 20

print(f"Age: {age}")

ValueError

A ValueError can occur when the type of operation is valid but the value provided is inappropriate.

For example:

number = int("hello")

Python knows how to convert a string containing digits into an integer, but "hello" is not a valid integer.

Another example:

number = int("25")

This works because "25" represents a valid integer.

ZeroDivisionError

Python cannot divide a number by zero.

result = 10 / 0

This produces a ZeroDivisionError.

This is particularly important when your program receives numbers from users or external data.

IndexError

An IndexError can happen when you try to access a list position that does not exist.

fruits = ["apple", "banana", "orange"]

print(fruits[5])

The list has indexes 0, 1 and 2. Index 5 does not exist.

KeyError

A KeyError can occur when you try to access a dictionary key that does not exist.

student = {
    "name": "Ada",
    "score": 85
}

print(student["age"])

There is no "age" key in the dictionary.

You can avoid this particular problem by using get() when appropriate.

print(student.get("age"))

If the key is missing, this returns None by default instead of raising a KeyError.

FileNotFoundError

You may encounter this error when your program tries to open a file that cannot be found.

with open("missing.txt", "r") as file:
    content = file.read()

If missing.txt does not exist in the expected location, Python raises a FileNotFoundError.

What Is an Exception?

An exception is a problem that occurs while a Python program is running.

For example:

number = int(input("Enter a number: "))

If the user enters:

hello

Python cannot convert that text into an integer and raises a ValueError.

Instead of allowing the program to stop abruptly, you can handle the expected exception.

The try and except Statements

The try block contains code that might produce an exception.

The except block tells Python what to do if a particular exception occurs.

try:
    number = int(input("Enter a number: "))
    print(number)

except ValueError:
    print("Please enter a valid number.")

If the user enters 25, the conversion succeeds.

If the user enters hello, Python executes the except block.

Think of it this way: "Try this. If this particular problem happens, respond this way instead of crashing."

Handle Specific Exceptions

It is generally better to catch the specific exception you expect.

try:
    number = int(input("Enter a number: "))

except ValueError:
    print("That is not a valid integer.")

This is more informative than catching every possible exception without knowing what happened.

Handling Multiple Exceptions

A program can have more than one except block.

try:

    number = int(input("Enter a number: "))
    result = 100 / number

    print(result)

except ValueError:

    print("Please enter a valid number.")

except ZeroDivisionError:

    print("You cannot divide by zero.")

Different problems can therefore receive different responses.

Getting Information About an Exception

You can store the exception in a variable using as.

try:

    number = int("hello")

except ValueError as error:

    print("Something went wrong:")
    print(error)

This can be useful while debugging because the exception object contains information about the problem.

The else Statement

You can use else when you want some code to run only if no exception occurred.

try:

    number = int(input("Enter a number: "))

except ValueError:

    print("Invalid number.")

else:

    print("You entered:", number)

The else block runs only when the try block succeeds.

The finally Statement

The finally block runs whether an exception occurs or not.

try:

    print("Program is running.")

except Exception:

    print("An error occurred.")

finally:

    print("This message runs either way.")

This can be useful for cleanup operations.

try + except + else + finally

Python allows these parts to work together.

try:

    number = int(input("Enter a number: "))

except ValueError:

    print("Invalid number.")

else:

    print("Number accepted:", number)

finally:

    print("Finished.")

Remember the general flow:

  1. try — attempt the operation.
  2. except — respond if an exception occurs.
  3. else — run if no exception occurs.
  4. finally — run regardless of the result.

Raising an Exception

Sometimes you want your own program to deliberately signal that something is wrong.

Python provides the raise statement for this.

age = -5

if age < 0:
    raise ValueError("Age cannot be negative.")

Here, the program detects an invalid value and raises a ValueError.

You will use this technique more as you start building larger programs.

Handling Invalid User Input

User input is one of the most common places where exceptions can occur.

Consider a program that asks for someone's age.

age = int(input("Enter your age: "))

If the user enters letters instead of a number, the program can fail.

A safer version is:

try:

    age = int(input("Enter your age: "))

    print("Your age is:", age)

except ValueError:

    print("Please enter your age as a number.")

Using Exceptions with Loops

You can combine exception handling with loops to keep asking the user until valid information is provided.

while True:

    try:

        age = int(input("Enter your age: "))
        break

    except ValueError:

        print("Please enter a valid number.")

print("Your age is:", age)

The loop continues when the user enters invalid information. Once a valid number is entered, break ends the loop.

This is a useful pattern for building interactive programs.

Using Errors to Debug Your Code

Debugging means finding and fixing problems in a program.

When Python gives you an error, do not immediately panic. Read the message carefully.

A typical traceback gives you useful information such as:

  • The type of exception.
  • The line where the problem occurred.
  • The operation Python was attempting.
  • Additional information about the problem.

For example:

ValueError: invalid literal for int()

The important part is ValueError. It tells you what kind of problem Python encountered.

Good habit: Read the last line of a traceback first. It often tells you the type of error and gives you the most immediate clue.

Why You Should Avoid a Bare except

You may sometimes see:

try:
    risky_code()

except:
    print("Something went wrong.")

This catches a very broad range of problems and can make debugging more difficult.

When possible, catch the specific exception you expect.

try:
    number = int(input("Enter a number: "))

except ValueError:
    print("Please enter a valid number.")

This makes your program easier to understand and maintain.

Custom Exceptions

Python also allows developers to create custom exception classes.

You do not need this technique for beginner programs, but it is useful to know that it exists.

class InvalidScoreError(Exception):
    pass

You could then raise your custom exception when appropriate.

score = 120

if score > 100:
    raise InvalidScoreError("Score cannot be greater than 100.")

Custom exceptions become more useful in larger applications where you want errors to clearly represent your application's own rules.

Errors & Exceptions in Real-World Programs

Exception handling is not just something you learn for programming exercises.

Real applications constantly deal with unexpected situations.

Student Management System

A student may enter a score such as:

abc

Your program can catch the invalid input instead of crashing.

File-Based Applications

A file might have been deleted or moved.

try:

    with open("students.txt", "r") as file:
        data = file.read()

except FileNotFoundError:

    print("The student file could not be found.")

Agricultural Systems

An agricultural application may receive unexpected sensor values or missing data.

Exception handling can help the application respond safely instead of stopping completely.

Robotics

A robotics program may communicate with hardware, read sensor data or process files. Problems can occur during any of these operations.

Proper error handling allows the software to detect certain problems and respond appropriately.

Common Beginner Mistakes

1. Ignoring the Error Message

Do not simply delete the line producing the error without understanding why it happened.

2. Catching Every Exception

Avoid using a broad except: when a specific exception is appropriate.

3. Putting Too Much Code Inside try

Keep the try block focused on the operation that might actually fail.

4. Using Exceptions Instead of Basic Logic

Exception handling is useful, but it should not replace ordinary validation and sensible program logic.

5. Thinking Errors Mean You Are Bad at Programming

Errors are part of programming.

Experienced developers encounter errors every day. The difference is that they have learned how to investigate and solve them.

Practice Exercises

Exercise 1 — Find the Syntax Error

if score > 50
    print("Pass")

Find and correct the problem.

Exercise 2 — Handle Invalid Numbers

Ask the user to enter a number. Use try and except to handle invalid input.

Exercise 3 — Division Calculator

Ask the user for two numbers and divide the first by the second.

Handle both invalid numbers and division by zero.

Exercise 4 — Safe File Reading

Try to open a file and handle FileNotFoundError if the file does not exist.

Exercise 5 — List Index

Create a list and ask the user for an index. Handle an IndexError if the index is outside the list.

Exercise 6 — Dictionary Lookup

Create a dictionary and ask the user for a key. Handle the situation where the key does not exist.

Exercise 7 — Keep Asking

Use a while loop and exception handling to keep asking for a number until the user enters a valid one.

Mini Project: Safe Number Calculator

Let's build a small calculator that handles common input problems.

while True:

    try:

        first = float(input("Enter the first number: "))
        second = float(input("Enter the second number: "))

        result = first / second

        print("Result:", result)

        break

    except ValueError:

        print("Please enter valid numbers.")

    except ZeroDivisionError:

        print("The second number cannot be zero.")

Notice how the program does not immediately crash when the user enters invalid information.

Instead, it explains the problem and gives the user another opportunity.

Challenge:

Improve the program so the user can choose between addition, subtraction, multiplication and division.

Then add exception handling for invalid menu choices and invalid numbers.

Python Errors & Exceptions Quiz

1. What does a syntax error usually mean?

2. Which exception commonly occurs when converting invalid text to an integer?

3. Which exception occurs when dividing by zero?

4. Which block contains code that might raise an exception?

5. What does except do?

6. When does an else block in try/except normally run?

7. Which block is designed to run whether an exception occurs or not?

8. Which exception can occur when accessing a list index that does not exist?

Python Errors & Exceptions Summary

Concept Meaning
SyntaxError Python cannot understand the structure of the code.
IndentationError Indentation is missing or incorrect.
NameError A name or variable cannot be found.
TypeError An operation is not valid for the given data types.
ValueError The value provided is inappropriate for the operation.
ZeroDivisionError An attempt was made to divide by zero.
IndexError A list or sequence index does not exist.
KeyError A dictionary key does not exist.
FileNotFoundError A requested file could not be found.
try Contains code that might raise an exception.
except Handles an exception.
else Runs when the try block succeeds.
finally Runs whether an exception occurs or not.
raise Manually raises an exception.
Key idea:

Errors are part of programming. Your goal is not to eliminate every possible error. Your goal is to understand what went wrong, fix your code, and handle expected problems properly.

Once you understand errors and exceptions, your programs can become much more reliable and easier to use.

```html id="3jv8pm"

Python Classes & Objects

You have now learned variables, data types, operators, conditions, loops, strings, lists, tuples, sets, dictionaries, functions, modules, file handling, and exception handling.

Now we are going to introduce one of the most important ideas in Python: Object-Oriented Programming, commonly called OOP.

OOP allows you to organize programs around objects that contain both data and behavior.

Simple idea: A class is like a blueprint. An object is something created from that blueprint.

For example, imagine you are building a program for a farm. You might have many sensors.

Instead of writing separate variables and functions for every sensor, you could create a Sensor class and then create many sensor objects from it.


What Is Object-Oriented Programming?

Object-Oriented Programming is a way of organizing software around objects.

An object can contain:

  • Data that describes it.
  • Functions that describe what it can do.

In OOP, the data inside an object is commonly represented by attributes, while functions associated with an object are called methods.

For example, a student object might have:

  • Name
  • Age
  • Score

And it might have methods such as:

  • Display information
  • Calculate a grade
  • Update a score

What Is a Class?

A class is a blueprint or template used to create objects.

Think about a building blueprint.

The blueprint describes how the building should be structured, but the blueprint itself is not the building.

Similarly, a class describes what an object should contain, while the actual object is created from that class.

class Student:
    pass

This creates a class called Student.

The pass statement simply tells Python that we intentionally have nothing else inside the class yet.

What Is an Object?

An object is an instance of a class.

Once you create a class, you can create objects from it.

class Student:
    pass

student1 = Student()
student2 = Student()

Here, student1 and student2 are two separate objects created from the same class.

Remember: Class = blueprint. Object = actual thing created from the blueprint.

Attributes

Attributes are pieces of data that belong to an object.

For example:

class Student:
    pass

student = Student()

student.name = "Ada"
student.age = 21
student.score = 85

print(student.name)
print(student.age)
print(student.score)

The object now has three attributes:

  • name
  • age
  • score

Although this works, there is a better way to initialize objects.

The __init__() Method

The __init__() method is commonly used to initialize an object when it is created.

class Student:

    def __init__(self, name, age, score):

        self.name = name
        self.age = age
        self.score = score

Now you can create a student like this:

student1 = Student("Ada", 21, 85)

print(student1.name)
print(student1.age)
print(student1.score)

When Student() is called, Python runs the __init__() method to initialize the object.

What Is self?

You will see the word self frequently when working with Python classes.

self refers to the particular object currently being worked with.

class Student:

    def __init__(self, name):

        self.name = name

When you create:

student1 = Student("Ada")

self.name refers to the name belonging to that particular student object.

If you create another object:

student2 = Student("John")

student2.name contains "John", while student1.name contains "Ada".

Methods

A method is a function that belongs to a class or object.

class Student:

    def __init__(self, name, score):

        self.name = name
        self.score = score

    def introduce(self):

        print(f"My name is {self.name}.")

student = Student("Ada", 85)

student.introduce()

The introduce() method belongs to the Student class.

You call it using the object:

student.introduce()

Methods Can Work With Attributes

One of the strengths of OOP is that methods can operate on the object's own data.

class Student:

    def __init__(self, name, score):

        self.name = name
        self.score = score

    def show_score(self):

        print(f"{self.name} scored {self.score}.")

student = Student("Ada", 85)

student.show_score()

The method accesses the attributes belonging to that object.

Changing Object Attributes

Object attributes can be changed after the object has been created.

class Student:

    def __init__(self, name, score):

        self.name = name
        self.score = score

student = Student("Ada", 85)

student.score = 92

print(student.score)

The score changes from 85 to 92.

Methods Can Change Object Data

You can also create methods that modify attributes.

class Student:

    def __init__(self, name, score):

        self.name = name
        self.score = score

    def update_score(self, new_score):

        self.score = new_score

student = Student("Ada", 85)

student.update_score(95)

print(student.score)

This keeps the operation inside the class.

Creating Multiple Objects

One class can be used to create many objects.

class Student:

    def __init__(self, name, score):

        self.name = name
        self.score = score

student1 = Student("Ada", 85)
student2 = Student("John", 78)
student3 = Student("Mary", 91)

print(student1.name)
print(student2.name)
print(student3.name)

All three objects follow the same class structure, but each object contains its own data.

Default Values in Classes

You can give an attribute a default value.

class Student:

    def __init__(self, name, score=0):

        self.name = name
        self.score = score

student = Student("Ada")

print(student.score)

Because no score was provided, Python uses the default value of 0.

Class Variables

A class variable is shared by objects created from the class.

class Student:

    school = "Python Academy"

    def __init__(self, name):

        self.name = name

student1 = Student("Ada")
student2 = Student("John")

print(student1.school)
print(student2.school)

Both objects can access the same class variable.

This is different from an instance attribute such as self.name, which normally belongs to a particular object.

Instance Variables vs Class Variables

Type Meaning
Instance variable Usually belongs to one specific object.
Class variable Shared at the class level.
class Student:

    school = "Python Academy"

    def __init__(self, name):

        self.name = name

Here, school is a class variable while name is an instance variable.

Inheritance

Inheritance allows one class to inherit attributes and methods from another class.

Think of it as creating a more specialized version of an existing class.

For example, imagine a general class called Animal.

class Animal:

    def speak(self):

        print("The animal makes a sound.")

Another class can inherit from it:

class Dog(Animal):
    pass

dog = Dog()

dog.speak()

The Dog class inherits the speak() method from Animal.

Parent and Child Classes

The class being inherited from is commonly called the parent class or base class.

The class doing the inheriting is commonly called the child class or derived class.

class Vehicle:

    def move(self):

        print("The vehicle is moving.")


class Car(Vehicle):
    pass

Here:

  • Vehicle is the parent class.
  • Car is the child class.

Adding Methods to a Child Class

A child class can have its own methods in addition to the methods inherited from its parent.

class Vehicle:

    def move(self):

        print("Vehicle is moving.")


class Car(Vehicle):

    def honk(self):

        print("Beep beep!")


car = Car()

car.move()
car.honk()

The object can use both the inherited method and its own method.

The super() Function

The super() function can be used to call behavior from a parent class.

class Animal:

    def __init__(self, name):

        self.name = name


class Dog(Animal):

    def __init__(self, name, breed):

        super().__init__(name)

        self.breed = breed


dog = Dog("Max", "Labrador")

print(dog.name)
print(dog.breed)

super() allows the child class to use the parent class's initialization logic.

Method Overriding

A child class can provide its own version of a method inherited from the parent class.

class Animal:

    def speak(self):

        print("Some animal sound.")


class Dog(Animal):

    def speak(self):

        print("Woof!")


dog = Dog()

dog.speak()

The Dog class replaces the inherited behavior of speak() with its own version.

Polymorphism

Polymorphism means that different objects can respond to the same method name in different ways.

class Dog:

    def speak(self):

        print("Woof!")


class Cat:

    def speak(self):

        print("Meow!")


animals = [Dog(), Cat()]

for animal in animals:

    animal.speak()

Both objects have a speak() method, but each object performs it differently.

You do not always need to know the exact object type before calling the method.

Encapsulation

Encapsulation is the idea of keeping related data and behavior together and controlling how the internal state of an object is accessed or changed.

Python does not enforce strict private fields in the same way some languages do, but naming conventions and properties can help communicate intended access.

You may see a leading underscore:

class BankAccount:

    def __init__(self, balance):

        self._balance = balance

The underscore convention communicates that _balance is intended for internal use.

Properties

A property allows you to control access to an attribute while keeping a simple attribute-like interface.

class Student:

    def __init__(self, score):

        self._score = score

    @property
    def score(self):

        return self._score

Properties become especially useful when you need validation or controlled access to data.

This is an advanced OOP feature, so do not worry if it feels unfamiliar at first.

The __str__() Method

Python provides special methods with names surrounded by double underscores. These are often called dunder methods.

One useful example is __str__().

class Student:

    def __init__(self, name, score):

        self.name = name
        self.score = score

    def __str__(self):

        return f"{self.name}: {self.score}"


student = Student("Ada", 85)

print(student)

The __str__() method defines a readable string representation of the object.

Other Special Methods

Python contains many special methods that allow objects to interact naturally with built-in operations.

Examples include:

  • __init__() — initialization.
  • __str__() — readable string representation.
  • __len__() — behavior for len().
  • __eq__() — behavior for equality comparison.

You do not need to memorize all special methods now. Learn them as you encounter situations where they are useful.

Composition

Another useful OOP idea is composition.

Composition means creating an object that contains another object.

For example, a robot can contain a sensor.

class Sensor:

    def read(self):

        return 42


class Robot:

    def __init__(self):

        self.sensor = Sensor()


robot = Robot()

print(robot.sensor.read())

The Robot object contains a Sensor object.

Composition is very useful when modeling systems made of multiple components.

Agricultural Example

Suppose you are developing an agricultural monitoring system.

You could represent a farm sensor using a class.

class SoilSensor:

    def __init__(self, location):

        self.location = location
        self.moisture = 0

    def update_moisture(self, value):

        self.moisture = value

    def show_reading(self):

        print(
            f"{self.location}: "
            f"{self.moisture}% soil moisture"
        )


sensor1 = SoilSensor("Field A")

sensor1.update_moisture(42)

sensor1.show_reading()

Now you can create another sensor without rewriting the entire structure.

sensor2 = SoilSensor("Field B")

sensor2.update_moisture(67)

sensor2.show_reading()

This is one reason OOP becomes powerful for larger projects.

Robotics Example

A robot can also be represented as an object.

class Robot:

    def __init__(self, name):

        self.name = name
        self.battery = 100

    def move(self):

        print(f"{self.name} is moving.")

    def show_battery(self):

        print(f"Battery: {self.battery}%")


robot = Robot("AgriBot")

robot.move()
robot.show_battery()

As your robotics knowledge grows, this basic idea can be expanded to represent motors, sensors, batteries and other components.

When Should You Use a Class?

Not every small Python program needs a class.

For a tiny calculation, a few variables and functions may be enough.

Classes become particularly useful when:

  • Your program has many related pieces of data.
  • You need many similar objects.
  • Objects have their own behavior.
  • Your application is becoming larger.
  • You want to organize complex systems.
Do not force OOP into everything. Use classes when they make your program easier to organize, understand and maintain.

Common Beginner Mistakes

1. Forgetting self

Instance methods normally need self as their first parameter.

class Student:

    def show_name(self):

        print(self.name)

2. Confusing a Class With an Object

The class is the blueprint. The object is an instance created from that blueprint.

3. Forgetting Parentheses When Creating an Object

student = Student()

You normally create an instance by calling the class.

4. Forgetting self When Accessing Attributes

class Student:

    def __init__(self, name):

        self.name = name

The attribute belongs to the object, so it is accessed using self.name inside the instance method.

5. Making Every Program a Class

Classes are useful, but they are not required for every small script.

Practice Exercises

Exercise 1 — Create a Person Class

Create a class called Person with a name and age.

Exercise 2 — Add a Method

Add a method called introduce() that prints the person's name and age.

Exercise 3 — Student Class

Create a Student class with:

  • Name
  • Course
  • Score

Add a method that displays the student's information.

Exercise 4 — Bank Account

Create a BankAccount class with a balance.

Add methods for depositing and withdrawing money.

Exercise 5 — Farm Sensor

Create a Sensor class with a location and moisture value.

Add a method that displays the sensor reading.

Exercise 6 — Robot

Create a Robot class with a name and battery level.

Add methods to move and display the battery level.

Exercise 7 — Inheritance

Create a parent class called Animal and a child class called Dog.

Give the parent a method and allow the child to inherit it.

Mini Project: Agricultural Sensor Manager

Let's combine classes and objects into a small agricultural monitoring example.

class Sensor:

    def __init__(self, location, moisture):

        self.location = location
        self.moisture = moisture

    def show_reading(self):

        print(
            f"Location: {self.location}"
        )

        print(
            f"Soil moisture: {self.moisture}%"
        )

    def update_moisture(self, new_value):

        self.moisture = new_value


sensor1 = Sensor("Field A", 35)

sensor1.show_reading()

sensor1.update_moisture(48)

print("Updated reading:")

sensor1.show_reading()

Now create another sensor:

sensor2 = Sensor("Field B", 72)

sensor2.show_reading()

Notice that we did not need to rewrite the class.

We simply created another object from the same blueprint.

Challenge:

Add a temperature attribute to the sensor.

Then create a method called show_all_readings() that displays both moisture and temperature.

Python Classes & Objects Quiz

1. What is a class?

2. What is an object?

3. Which method is commonly used to initialize an object?

4. What does self usually refer to?

5. What is a method?

6. What does inheritance allow?

7. Which function is commonly used to access parent-class behavior?

8. What is an attribute?

Python Classes & Objects Summary

Concept Meaning
Class A blueprint used to create objects.
Object An instance created from a class.
Attribute Data associated with an object.
Method A function associated with a class or object.
__init__() Commonly used to initialize objects.
self Refers to the current object in an instance method.
Inheritance Allows a class to inherit from another class.
super() Provides access to parent-class behavior.
Polymorphism Allows different objects to respond to the same operation in different ways.
Encapsulation Organizes and controls access to an object's data and behavior.
Composition Builds objects using other objects as components.
Key idea:

Classes allow you to create reusable blueprints, while objects allow you to create individual instances of those blueprints.

This becomes especially powerful when building larger systems such as agricultural applications, robotics software, management systems and other complex programs.

17. Python Date & Time

Programs often need to work with dates and times. For example, a program may need to record when a user registered, calculate someone's age, determine how many days remain until an event, or display the current date and time.

Python provides the built-in datetime module for working with dates and times.

What you will learn:
  • How Python represents dates and times
  • How to get the current date and time
  • How to create specific dates
  • How to access parts of a date
  • How to format dates and times
  • How to compare dates
  • How to calculate differences between dates
  • How to work with time intervals
  • How to build practical date and time programs

Python Date & Time: Introduction

Python does not treat a date such as September 5, 2026 as ordinary text when you use the datetime module.

Instead, Python can represent the date as a structured object containing information such as the year, month and day.

This makes it possible to perform operations on dates rather than simply displaying them.

For example, Python can help you answer questions such as:

  • What is today's date?
  • What time is it?
  • What date will it be 30 days from now?
  • How many days are between two dates?
  • What day of the week was a particular date?

The datetime Module

The first thing you need to do when working with Python dates and times is import the datetime module.

import datetime

Python's datetime module contains several useful classes for working with dates and times.

The most commonly used ones are:

  • date — represents a calendar date.
  • time — represents a time.
  • datetime — represents both a date and a time.
  • timedelta — represents a difference or duration between dates or times.

Getting the Current Date

You can use datetime.date.today() to get today's date.

import datetime

today = datetime.date.today()

print(today)

The result may look like:

2026-09-05

Python displays the date in the format:

YYYY-MM-DD

For example:

  • 2026 = year
  • 09 = month
  • 05 = day

Getting the Year, Month and Day

Once you have a date object, you can access individual parts of the date.

import datetime

today = datetime.date.today()

print(today.year)
print(today.month)
print(today.day)

Each part is accessed using an attribute:

  • .year gives the year.
  • .month gives the month.
  • .day gives the day.

This is useful when you need to use only one part of a date.

Creating a Specific Date

You can create a specific date using datetime.date().

import datetime

birthday = datetime.date(2000, 5, 12)

print(birthday)

Here:

  • 2000 is the year.
  • 5 is the month.
  • 12 is the day.

The result is:

2000-05-12

Getting the Current Date and Time

If you need both the current date and current time, you can use datetime.datetime.now().

import datetime

now = datetime.datetime.now()

print(now)

A result might look like:

2026-09-05 12:15:30.123456

The value contains the year, month, day, hour, minute, second and microseconds.

Accessing Parts of a datetime

A datetime object contains both date and time information. You can access each part individually.

import datetime

now = datetime.datetime.now()

print(now.year)
print(now.month)
print(now.day)

print(now.hour)
print(now.minute)
print(now.second)

The time-related attributes include:

  • .hour
  • .minute
  • .second
  • .microsecond

Creating a Time Object

Python can also represent a time without a date.

import datetime

meeting_time = datetime.time(14, 30, 0)

print(meeting_time)

The result is:

14:30:00

The arguments represent:

  • Hour
  • Minute
  • Second

For example, 14:30:00 represents 2:30 PM using the 24-hour clock.

Formatting Dates with strftime()

Sometimes the default date format is not suitable for users.

For example, Python may display:

2026-09-05

But you may want to display:

05 September 2026

The strftime() method allows you to format a date or time.

import datetime

today = datetime.date.today()

formatted_date = today.strftime("%d %B %Y")

print(formatted_date)

The result could be:

05 September 2026

Common strftime() Format Codes

The symbols used inside strftime() tell Python how the date should be displayed.

Code Meaning Example
%Y Four-digit year 2026
%y Two-digit year 26
%m Month as a number 09
%B Full month name September
%b Short month name Sep
%d Day of the month 05
%A Full weekday name Saturday
%a Short weekday name Sat
%H Hour using 24-hour clock 14
%I Hour using 12-hour clock 02
%M Minute 30
%S Second 45
%p AM or PM PM

For example:

import datetime

now = datetime.datetime.now()

print(now.strftime("%A, %d %B %Y"))
print(now.strftime("%I:%M %p"))

Converting Text into a Date

Sometimes a date starts as a string.

date_text = "05/09/2026"

Python sees this as text. If you want to perform date calculations on it, you can convert it into a datetime object using strptime().

import datetime

date_text = "05/09/2026"

date_object = datetime.datetime.strptime(
    date_text,
    "%d/%m/%Y"
)

print(date_object)

strptime() means that Python is parsing a string according to the format you provide.

Finding the Day of the Week

You can use weekday() to determine the weekday number of a date.

import datetime

date = datetime.date(2026, 9, 5)

print(date.weekday())

Python numbers weekdays from 0 to 6:

  • 0 = Monday
  • 1 = Tuesday
  • 2 = Wednesday
  • 3 = Thursday
  • 4 = Friday
  • 5 = Saturday
  • 6 = Sunday

You can also use strftime() when you want the actual weekday name.

import datetime

date = datetime.date(2026, 9, 5)

print(date.strftime("%A"))

Using timedelta

The timedelta class represents a period of time.

It is especially useful when you want to add or subtract time from a date.

import datetime

today = datetime.date.today()

future_date = today + datetime.timedelta(days=7)

print(future_date)

The program calculates the date seven days after today.

You can also subtract days:

import datetime

today = datetime.date.today()

previous_date = today - datetime.timedelta(days=7)

print(previous_date)

Calculating Dates

Date calculations become useful in real programs.

For example, imagine a subscription that lasts 30 days.

import datetime

start_date = datetime.date.today()
end_date = start_date + datetime.timedelta(days=30)

print("Start:", start_date)
print("End:", end_date)

Notice that timedelta(days=30) means exactly 30 days. It does not mean "the same day next month," because calendar months have different numbers of days.

Finding the Difference Between Dates

You can subtract one date from another.

import datetime

start = datetime.date(2026, 9, 1)
end = datetime.date(2026, 9, 20)

difference = end - start

print(difference)

The result is:

19 days, 0:00:00

You can access the number of days directly using .days.

print(difference.days)

Result:

19

Calculating Age with Dates

Dates can be used to build practical programs such as an age calculator.

A simple approach is to compare the person's birth year with the current year.

import datetime

birth_year = 2000
current_year = datetime.date.today().year

age = current_year - birth_year

print("Approximate age:", age)

This gives an approximate age based only on the year. A precise age calculation should also check whether the person's birthday has already occurred this year.

This is an important programming lesson: the simplest calculation is not always the most accurate calculation.

Comparing Dates

Date objects can be compared using the same comparison operators you learned earlier.

import datetime

today = datetime.date.today()
deadline = datetime.date(2026, 12, 31)

if today < deadline:
    print("The deadline has not passed.")
else:
    print("The deadline has passed.")

You can use operators such as:

  • <
  • >
  • <=
  • >=
  • ==
  • !=

Working with User Date Input

When a user enters a date, Python initially receives it as a string.

date_text = input("Enter a date (DD/MM/YYYY): ")

You can convert that text into a date using strptime().

import datetime

date_text = input("Enter a date (DD/MM/YYYY): ")

date = datetime.datetime.strptime(
    date_text,
    "%d/%m/%Y"
).date()

print("You entered:", date)

This technique is useful for applications that accept birthdays, appointments, deadlines and booking dates.

date vs datetime

It is important to understand the difference between these two objects.

Object Contains
date Year, month and day
time Hour, minute, second and microsecond
datetime Both date and time
timedelta A duration or difference

Choosing the appropriate object makes your code easier to understand.

Using an Import Alias

You can give an imported module a shorter name using as.

import datetime as dt

today = dt.date.today()

print(today)

This can make your code shorter, especially when you use a module many times.

Understanding Time Zones

Time becomes more complicated when your application is used in different parts of the world.

For example, a meeting scheduled for 10:00 AM in Nigeria does not happen at the same local time everywhere else.

Python can work with time-zone-aware dates and times. Modern Python code can use the zoneinfo module for IANA time zones.

from datetime import datetime
from zoneinfo import ZoneInfo

lagos_time = datetime.now(ZoneInfo("Africa/Lagos"))

print(lagos_time)

A time-zone-aware datetime contains information about the time zone, making it safer for applications that work across different locations.

Time zones are particularly important for travel applications, appointment systems, online meetings, financial systems and distributed software.

Understanding UTC

UTC stands for Coordinated Universal Time. It is commonly used as a reference point when applications work with multiple time zones.

A common software practice is to store timestamps in UTC and convert them to the user's local time when displaying them.

The important idea is not to assume that every user's clock is in the same time zone.

Date & Time in the Real World

Date and time handling appears in many types of software.

  • Booking systems: store appointment dates and times.
  • Travel applications: calculate departure and arrival times.
  • School systems: record registration and examination dates.
  • Banking systems: record transaction timestamps.
  • Web applications: display when posts or accounts were created.
  • Robotics: record when sensors collected measurements.
  • Agriculture: track planting, irrigation and harvesting dates.

For example, an agricultural monitoring system might record the time a sensor detected a change in soil conditions.

from datetime import datetime

reading_time = datetime.now()

print("Sensor reading recorded at:", reading_time)

Common Date & Time Mistakes

1. Treating dates as ordinary strings

Strings can display dates, but they are not ideal for date calculations.

date = "2026-09-05"

If you need to calculate with the date, convert it into a proper date or datetime object.

2. Confusing date and datetime

A date does not contain a time. A datetime contains both.

3. Forgetting date format codes

When using strftime() or strptime(), make sure your format matches the actual date.

For example, 05/09/2026 interpreted as %d/%m/%Y means 5 September 2026.

4. Ignoring time zones

Applications used internationally should not assume that every timestamp belongs to the same local time zone.

5. Assuming every month has the same number of days

A month can contain 28, 29, 30 or 31 days. For calendar-month calculations, do not simply assume that every month contains 30 days.

Practice Exercises

Try these exercises yourself before looking for a solution.

Exercise 1: Current Date

Write a program that prints today's date.

Exercise 2: Current Time

Write a program that prints the current date and time.

Exercise 3: Birthday

Create a date object representing your birthday and print it.

Exercise 4: Formatted Date

Display today's date in this format:

05 September 2026

Exercise 5: Seven Days Later

Write a program that calculates the date seven days from today.

Exercise 6: Days Until an Event

Create a future date and calculate how many days remain until that date.

Exercise 7: User Date

Ask the user to enter a date in DD/MM/YYYY format and convert it into a Python date.

Mini Project: Appointment Countdown

Let's combine several things you have learned to create a small appointment countdown program.

The program asks the user for an appointment date and calculates how many days remain.

import datetime

date_text = input("Enter appointment date (DD/MM/YYYY): ")

appointment = datetime.datetime.strptime(
    date_text,
    "%d/%m/%Y"
).date()

today = datetime.date.today()

if appointment < today:
    print("This appointment date has already passed.")
elif appointment == today:
    print("The appointment is today!")
else:
    days_remaining = (appointment - today).days
    print("Days remaining:", days_remaining)

This small project combines:

  • input()
  • Strings
  • datetime
  • strptime()
  • date.today()
  • Conditions
  • Date subtraction
  • .days

Notice how concepts from earlier modules begin to work together. This is an important stage in learning programming: you are no longer learning individual features in isolation.

Python Date & Time Quiz

Test yourself before continuing.

1. Which module is commonly used to work with dates and times?



2. Which function gets today's date?



3. What does strftime() do?



4. Which class represents a duration or difference between dates?



5. What does %Y usually represent in a date format?





Python Date & Time Summary

The datetime module gives Python the ability to work with calendar dates, times and durations.

In this lesson, you learned how to:

  • Import the datetime module.
  • Get today's date.
  • Get the current date and time.
  • Create specific dates and times.
  • Access individual date and time components.
  • Format dates using strftime().
  • Convert strings into dates using strptime().
  • Compare dates.
  • Calculate differences between dates.
  • Add and subtract periods using timedelta.
  • Understand the importance of time zones.
Key idea: Dates and times become much more useful when you stop treating them as ordinary text and start working with Python's date and time objects.

18. Python Regular Expressions

Regular expressions, often called regex, are patterns used to search and manipulate text.

Imagine you have a large piece of text containing hundreds of email addresses. Instead of checking every character manually, you can give Python a pattern and ask it to find text that follows that pattern.

Regular expressions can be used to:

  • Search for specific patterns in text.
  • Check whether text follows a particular format.
  • Extract information from larger pieces of text.
  • Replace matching text.
  • Split text using patterns.
  • Validate things such as usernames, phone numbers and email-like strings.
Important: Regular expressions can look complicated at first. Do not try to memorize every symbol. Learn what the common symbols mean and practice building patterns gradually.

What Are Regular Expressions?

A regular expression is a pattern that describes text you want Python to find.

For example, suppose we have:

text = "My phone number is 08012345678"

We could create a pattern that looks for a sequence of digits.

\d+

The pattern tells Python to look for one or more digits.

This allows us to find 08012345678 without knowing the exact number beforehand.

The re Module

Python provides regular expression functionality through the built-in re module.

import re

Once imported, you can use functions such as:

  • re.search()
  • re.match()
  • re.findall()
  • re.finditer()
  • re.sub()
  • re.split()

Why Regex Patterns Often Use r""

You will often see regular expression patterns written using a raw string.

pattern = r"\d+"

The r before the quotation mark tells Python to treat the string as a raw string.

This is particularly useful because regular expressions make heavy use of backslashes.

For example:

r"\d+"
r"\w+"
r"\s+"

Using raw strings makes regex patterns easier to write and read.

re.match()

re.match() checks for a match at the beginning of the string.

import re

text = "Python is fun."

result = re.match("Python", text)

if result:
    print("The text starts with Python.")

Compare this with re.search(): search() can find the pattern anywhere in the string, while match() checks the beginning.

re.findall()

re.findall() finds all occurrences of a pattern and returns them as a list.

import re

text = "Python is easy. Python is powerful."

matches = re.findall("Python", text)

print(matches)

The result is:

['Python', 'Python']

This is useful when you need every occurrence rather than only the first one.

Finding Digits with \d

One of the most useful regex symbols is \d.

It represents a digit from 0 to 9.

import re

text = "I have 3 apples and 12 oranges."

numbers = re.findall(r"\d+", text)

print(numbers)

The result is:

['3', '12']

The + means "one or more".

Therefore, \d+ means one or more consecutive digits.

Common Regex Character Classes

Pattern Meaning
\d A digit
\D A non-digit
\w A word character
\W A non-word character
\s Whitespace
\S Non-whitespace
. Almost any character except a newline

The + Quantifier

The + symbol means one or more occurrences of the preceding pattern.

r"\d+"

This finds one or more consecutive digits.

For example:

import re

text = "Room 12, floor 3."

numbers = re.findall(r"\d+", text)

print(numbers)

Result:

['12', '3']

The * Quantifier

The * symbol means zero or more occurrences of the preceding pattern.

r"\d*"

This can match even when no digit exists, so you need to use it carefully.

The ? Quantifier

The ? symbol means zero or one occurrence of the preceding pattern.

This is useful when part of a pattern is optional.

r"colou?r"

This pattern can match both:

color
colour

Exact Repetitions with {}

Curly brackets allow you to specify how many times something should occur.

r"\d{4}"

This means exactly four digits.

For example:

import re

text = "Year 2026"

result = re.findall(r"\d{4}", text)

print(result)

Result:

['2026']

You can also specify a range.

r"\d{2,4}"

This means between two and four digits.

Character Sets with []

Square brackets allow you to specify a set of characters.

r"[abc]"

This matches one character that is either a, b or c.

You can also specify a range:

r"[a-z]"

This matches a lowercase letter from a to z.

For digits:

r"[0-9]"

Negated Character Sets

A caret ^ inside square brackets means "not these characters."

r"[^0-9]"

This matches a character that is not a digit.

^ and $ Anchors

The caret and dollar sign have another important use outside character sets.

  • ^ means the beginning of the string.
  • $ means the end of the string.

For example:

r"^Python"

This means the string must begin with "Python".

And:

r"Python$"

means the string must end with "Python".

Groups with Parentheses

Parentheses allow you to group parts of a pattern.

r"(cat|dog)"

This pattern can match either cat or dog.

The vertical bar | means "or".

import re

text = "I have a cat and a dog."

animals = re.findall(r"(cat|dog)", text)

print(animals)

Result:

['cat', 'dog']

Finding Email-Like Patterns

Regular expressions can be used to locate text that resembles an email address.

import re

text = "Contact us at hello@example.com."

pattern = r"[\w.-]+@[\w.-]+\.\w+"

emails = re.findall(pattern, text)

print(emails)

Result:

['hello@example.com']

This pattern is useful for learning how regex works, but real-world email validation can be more complicated than a single regex.

Finding Phone Numbers

You can also use regex to locate sequences that resemble phone numbers.

import re

text = "Call 08012345678 for more information."

pattern = r"\b\d{11}\b"

numbers = re.findall(pattern, text)

print(numbers)

Here, \b represents a word boundary and \d{11} means exactly eleven digits.

Replacing Text with re.sub()

Regular expressions are not only for finding text. They can also replace matching text.

import re

text = "Python is difficult."

new_text = re.sub(
    "difficult",
    "powerful",
    text
)

print(new_text)

Result:

Python is powerful.

The general structure is:

re.sub(pattern, replacement, text)

Removing Numbers

You can combine re.sub() with a regex pattern to remove unwanted characters.

import re

text = "Python123"

clean_text = re.sub(r"\d", "", text)

print(clean_text)

Result:

Python

Splitting Text with re.split()

re.split() splits text wherever the pattern occurs.

import re

text = "apple,banana;orange"

items = re.split(r"[,;]", text)

print(items)

Result:

['apple', 'banana', 'orange']

This is useful when information may be separated by different delimiters.

Regex Flags

Regex functions can accept flags that change how the pattern behaves.

One useful flag is re.IGNORECASE.

import re

text = "Python PYTHON python"

matches = re.findall(
    "python",
    text,
    re.IGNORECASE
)

print(matches)

This allows the pattern to match different combinations of uppercase and lowercase letters.

Compiling a Regex Pattern

If you are going to use the same pattern multiple times, you can compile it into a regex object.

import re

pattern = re.compile(r"\d+")

print(pattern.findall("There are 12 apples and 5 oranges."))

This can make repeated use of the same pattern cleaner.

Understanding Match Objects

Functions such as re.search() can return a match object.

import re

text = "My score is 95."

result = re.search(r"\d+", text)

if result:
    print(result.group())

Result:

95

The group() method retrieves the text that matched the pattern.

Using re.finditer()

re.finditer() returns an iterator containing match objects for every match.

import re

text = "Scores: 75, 82, 91"

for match in re.finditer(r"\d+", text):
    print(match.group())

Output:

75
82
91

This becomes particularly useful when you need additional information about each match, such as where it appears in the text.

Using Regex for Validation

Validation means checking whether data follows rules you expect.

For example, suppose you want a username containing only letters, numbers and underscores.

import re

username = input("Enter username: ")

if re.fullmatch(r"[A-Za-z0-9_]+", username):
    print("Valid username.")
else:
    print("Invalid username.")

fullmatch() requires the entire string to match the pattern.

This is often more appropriate for validation than simply searching for a matching part of the input.

re.fullmatch()

re.fullmatch() checks whether the entire string matches a pattern.

import re

text = "12345"

if re.fullmatch(r"\d+", text):
    print("The entire string contains digits.")

This is different from searching for digits somewhere inside the string.

Regular Expressions in the Real World

Regex is useful in many areas of software development.

  • Extracting information from documents.
  • Validating form input.
  • Finding phone numbers.
  • Finding email-like strings.
  • Cleaning datasets.
  • Processing log files.
  • Searching source code.
  • Preparing data for analysis.

For example, imagine an agricultural monitoring system producing logs:

Sensor A12: temperature=31.5
Sensor B07: temperature=29.8
Sensor C03: temperature=30.1

A regular expression could help extract the sensor identifiers or temperature values automatically.

import re

text = """
Sensor A12: temperature=31.5
Sensor B07: temperature=29.8
Sensor C03: temperature=30.1
"""

temperatures = re.findall(
    r"temperature=(\d+\.\d+)",
    text
)

print(temperatures)

Result:

['31.5', '29.8', '30.1']

This is a simple example of how regex can become useful in data processing and automation.

Common Regex Mistakes

1. Forgetting that regex is pattern-based

A regex pattern should describe what you are looking for rather than simply containing the exact text you expect every time.

2. Confusing + and *

+ means one or more. * means zero or more.

3. Forgetting raw strings

Using r"..." is often clearer when writing patterns containing backslashes.

4. Using search() when you need full validation

search() can find a valid-looking part of a string. For validation, fullmatch() is often a better choice.

5. Making patterns unnecessarily complicated

Start with the simplest pattern that solves the problem. Complicated regular expressions can become difficult to understand and maintain.

Practice Exercises

Exercise 1: Find Numbers

Given the text below, use regex to find all numbers.

text = "I bought 5 books, 2 pens and 10 notebooks."

Exercise 2: Find Python

Find every occurrence of the word "Python" in a string.

Exercise 3: Replace Digits

Remove all digits from:

"Python123"

Exercise 4: Extract Phone Numbers

Find an eleven-digit phone number inside a larger string.

Exercise 5: Validate Username

Create a pattern that allows only letters, numbers and underscores in a username.

Exercise 6: Extract Temperatures

From this text, extract all temperature values:

temperature=31.5
temperature=28.9
temperature=30.2

Mini Project: Information Extractor

Let's build a small program that extracts email-like addresses and phone numbers from a piece of text.

import re

text = """
Contact us at hello@example.com
or support@example.org.
Call 08012345678 for assistance.
"""

email_pattern = r"[\w.-]+@[\w.-]+\.\w+"
phone_pattern = r"\b\d{11}\b"

emails = re.findall(email_pattern, text)
phones = re.findall(phone_pattern, text)

print("Emails:")
for email in emails:
    print(email)

print("\nPhone numbers:")
for phone in phones:
    print(phone)

This project combines strings, lists, loops, functions from the re module and regular expression patterns.

Python Regular Expressions Quiz

1. Which module provides Python's regular expression tools?



2. What does \d represent?



3. Which function finds all matches?



4. What does + mean in a regex pattern?



5. Which function can replace matching text?





Regular Expressions Summary

Regular expressions allow Python to work with patterns in text.

You learned how to:

  • Import the re module.
  • Search for patterns with re.search().
  • Check the beginning of text with re.match().
  • Find all matches with re.findall().
  • Use \d, \w and \s.
  • Use quantifiers such as +, * and ?.
  • Use exact repetitions with {}.
  • Create character sets using [].
  • Use ^ and $ as anchors.
  • Group patterns with parentheses.
  • Replace text using re.sub().
  • Split text using re.split().
  • Validate complete strings with re.fullmatch().
  • Use regex for practical data extraction.
Key idea: Regex is a language for describing text patterns. You do not need to memorize every regex symbol immediately. Learn the common patterns, practice them, and build more complicated expressions as your projects require them.

19. Python Iterators & Generators

You have already used for loops many times in this course. For example:

numbers = [10, 20, 30, 40]

for number in numbers:
    print(number)

But what actually happens behind the scenes when Python goes through the list one item at a time?

This is where iterators come in.

Generators take this idea even further. They allow you to produce values one at a time instead of creating all the values in memory at once.

What you will learn:
  • What iteration means
  • What iterators are
  • How iter() works
  • How next() works
  • What StopIteration means
  • How to create your own iterator
  • What generators are
  • How yield works
  • Generator functions
  • Generator expressions
  • Why generators are useful for large datasets

What Is Iteration?

Iteration means going through items one at a time.

You have already been doing this with loops.

fruits = ["apple", "banana", "orange"]

for fruit in fruits:
    print(fruit)

Python takes the items from the collection one after another.

The important idea is:

Iteration = processing items one at a time.

What Is an Iterable?

An iterable is an object that Python can go through one item at a time.

Common examples include:

  • Lists
  • Tuples
  • Strings
  • Sets
  • Dictionaries
  • Files

For example, a string is iterable:

word = "Python"

for letter in word:
    print(letter)

Output:

P
y
t
h
o
n

Python can therefore move through the characters of the string one at a time.

What Is an Iterator?

An iterator is an object that keeps track of where it is during iteration and produces the next value when requested.

You can obtain an iterator from an iterable using iter().

numbers = [10, 20, 30]

iterator = iter(numbers)

print(iterator)

The iterator remembers the current position in the sequence.

The iter() Function

The iter() function creates an iterator from an iterable.

fruits = ["apple", "banana", "orange"]

fruit_iterator = iter(fruits)

The list is the iterable, while fruit_iterator is the iterator.

You can then request values from the iterator using next().

The next() Function

The next() function asks an iterator for its next value.

fruits = ["apple", "banana", "orange"]

iterator = iter(fruits)

print(next(iterator))
print(next(iterator))
print(next(iterator))

Output:

apple
banana
orange

Every time next() is called, the iterator moves forward.

StopIteration

What happens when there are no more values?

fruits = ["apple", "banana"]

iterator = iter(fruits)

print(next(iterator))
print(next(iterator))
print(next(iterator))

The third call has no value to return. Python raises a StopIteration exception.

This tells Python that the iterator has reached the end.

You normally do not see this exception when using a regular for loop because Python handles the iteration process for you.

How for Loops Use Iterators

A for loop handles much of the iterator work automatically.

numbers = [10, 20, 30]

for number in numbers:
    print(number)

Conceptually, Python obtains an iterator, repeatedly requests the next value and stops when the iterator is exhausted.

You do not normally need to manually call iter() and next() when writing ordinary loops.

The Iterator Protocol

Python iterators follow a simple protocol.

An iterator provides:

  • __iter__() — returns the iterator itself.
  • __next__() — returns the next value.

These are special methods, sometimes called dunder methods because their names begin and end with double underscores.

You do not need to memorize the implementation immediately. The important idea is that Python knows how to ask an iterator for its next value.

Creating Your Own Iterator

Because you have already learned classes, you can create a custom iterator using a class.

class CountUp:

    def __init__(self, maximum):
        self.current = 1
        self.maximum = maximum

    def __iter__(self):
        return self

    def __next__(self):

        if self.current <= self.maximum:
            value = self.current
            self.current += 1
            return value

        raise StopIteration


counter = CountUp(5)

for number in counter:
    print(number)

Output:

1
2
3
4
5

This example demonstrates the iterator protocol directly.

What Are Generators?

A generator is a convenient way to create an iterator.

Instead of manually creating a class with __iter__() and __next__(), you can often write a generator function using yield.

This makes many iterator problems much simpler.

The yield Keyword

The yield keyword produces a value from a generator.

def count_up():
    yield 1
    yield 2
    yield 3

Calling the function does not immediately produce all three values.

numbers = count_up()

print(next(numbers))
print(next(numbers))
print(next(numbers))

Output:

1
2
3

After yielding a value, the generator pauses. When next() is called again, it continues from where it stopped.

Generator Functions

A function containing yield is called a generator function.

def numbers():
    yield 10
    yield 20
    yield 30

for number in numbers():
    print(number)

Output:

10
20
30

The generator produces each value as the loop requests it.

yield vs return

You already learned about return in functions. It is important to understand how it differs from yield.

return yield
Returns a value and ends the function. Produces a value and pauses the generator.
Normally produces one result. Can produce many values over time.
Used in ordinary functions. Used to create generators.

For example:

def normal_function():
    return 1

Compared with:

def generator_function():
    yield 1
    yield 2
    yield 3

Generators Remember Their State

One of the most useful features of a generator is that it remembers where it stopped.

def count():

    number = 1

    while number <= 3:
        yield number
        number += 1


counter = count()

print(next(counter))
print(next(counter))
print(next(counter))

The generator remembers the value of number between calls.

This is what allows the generator to pause and continue later.

Using Generators with for Loops

Most of the time, you will use generators with for loops.

def count_up_to(limit):

    number = 1

    while number <= limit:
        yield number
        number += 1


for number in count_up_to(5):
    print(number)

Output:

1
2
3
4
5

Why Generators Can Save Memory

Consider a program that needs to work with one million numbers.

A list stores all of the numbers in memory:

numbers = [number for number in range(1000000)]

A generator can produce the numbers one at a time:

numbers = (number for number in range(1000000))

The generator does not need to create the entire sequence as a list at once.

Key idea: Generators are especially useful when you have large amounts of data and only need to process one item at a time.

Generator Expressions

Generator expressions look similar to list comprehensions.

A list comprehension creates a list:

squares = [number * number for number in range(5)]

print(squares)

A generator expression uses parentheses:

squares = (number * number for number in range(5))

for square in squares:
    print(square)

The generator produces each square when it is requested.

List vs Generator

List Generator
Stores generated values. Produces values when requested.
Can be indexed directly. Does not work like a normal indexed list.
Can consume more memory for large sequences. Can be much more memory-efficient.
Useful when you need all values available. Useful when processing values one at a time.

Generators and Large Files

Generators become particularly useful when processing large files.

Instead of loading an entire file into memory, you can process one line at a time.

def read_lines(filename):

    with open(filename, "r", encoding="utf-8") as file:

        for line in file:
            yield line.strip()


for line in read_lines("large_data.txt"):
    print(line)

The generator yields one line at a time.

This approach can be useful when dealing with very large datasets.

Filtering Data with a Generator

Generators can also be used to process only the information you need.

def even_numbers(numbers):

    for number in numbers:

        if number % 2 == 0:
            yield number


values = [1, 2, 3, 4, 5, 6]

for number in even_numbers(values):
    print(number)

Output:

2
4
6

The generator does not need to create another list containing all the even numbers.

Generator Pipelines

Generators can be combined to create processing pipelines.

Imagine that you have thousands of sensor readings. One generator could read the data, another could filter it, and another could transform it.

def numbers():
    for number in range(1, 11):
        yield number


def even_numbers(values):
    for value in values:
        if value % 2 == 0:
            yield value


def squared(values):
    for value in values:
        yield value * value


data = squared(even_numbers(numbers()))

for value in data:
    print(value)

Output:

4
16
36
64
100

Notice that the data flows through the stages instead of requiring every intermediate result to be stored in a large list.

Infinite Generators

A generator can theoretically produce values forever.

def counter():

    number = 1

    while True:
        yield number
        number += 1

This generator does not have a natural stopping point.

You must therefore control how many values you request.

numbers = counter()

for _ in range(5):
    print(next(numbers))

Output:

1
2
3
4
5

Be careful with infinite generators. A loop that tries to consume the entire generator will never finish.

return in a Generator

A generator can also contain return.

def example():

    yield 1
    yield 2

    return

    yield 3

Once the generator reaches return, it stops producing values.

In ordinary beginner programs, you will usually use yield to produce values and allow the generator to finish naturally.

When Should You Use Generators?

Generators are particularly useful when:

  • You are processing a large amount of data.
  • You only need one item at a time.
  • You are reading large files.
  • You are processing streams of information.
  • You want to create a sequence lazily.
  • You want to build data-processing pipelines.

A generator is not automatically better than a list. If you need to access values repeatedly by index, a list may be more appropriate.

Generators in Agriculture and Data Processing

Generators become especially interesting when working with large datasets.

Imagine a farm monitoring system collecting thousands of temperature readings.

def sensor_readings():

    readings = [
        29.5,
        31.2,
        30.8,
        28.9,
        32.1
    ]

    for reading in readings:
        yield reading


for temperature in sensor_readings():

    if temperature > 30:
        print("High temperature:", temperature)

In a real agricultural system, the readings might come from sensors, files, databases or network streams instead of a small list.

The same generator concept can still be used to process each reading as it arrives.

Generators in Robotics

Robotics systems often process streams of information continuously.

A generator can represent a sequence of sensor readings.

def sensor_data():

    for value in [20, 21, 22, 23, 24]:
        yield value


for reading in sensor_data():

    print("Sensor reading:", reading)

In a real robot, the values could instead come from sensors such as temperature, distance, light or soil-moisture sensors.

This is one reason iterators and generators are worth understanding if you are interested in automation, robotics or data processing.

Common Mistakes

1. Calling next() after the iterator is exhausted

Once an iterator has no more values, calling next() can raise StopIteration.

2. Expecting a generator to behave exactly like a list

A generator produces values as needed. It does not provide the same indexing and repeated-access behavior as a list.

3. Accidentally consuming a generator

numbers = (number for number in range(5))

print(list(numbers))
print(list(numbers))

The second result will be empty because the generator has already been exhausted.

4. Creating an uncontrolled infinite loop

Infinite generators can be useful, but you must control how many values you consume.

5. Using generators when you actually need a reusable collection

If you need to repeatedly access all the values, a list may be a better choice.

Practice Exercises

Exercise 1: Use iter()

Create a list of three fruits and use iter() to create an iterator.

Exercise 2: Use next()

Use next() to retrieve each fruit from your iterator.

Exercise 3: Create a Generator

Write a generator that produces the numbers 1 through 5.

Exercise 4: Even Numbers

Create a generator that produces only even numbers from 1 to 20.

Exercise 5: Squares

Create a generator that produces the square of each number from 1 to 10.

Exercise 6: Countdown

Create a generator that counts backward from a number supplied by the user.

Exercise 7: Temperature Filter

Create a generator that receives temperature readings and yields only readings above 30 degrees.

Mini Project: Sensor Data Processor

Let's create a small generator that processes sensor readings and produces only readings above a chosen threshold.

def high_temperature_readings(readings, threshold):

    for reading in readings:

        if reading > threshold:
            yield reading


temperatures = [
    27.5,
    31.2,
    29.8,
    33.4,
    30.1,
    26.9
]

for temperature in high_temperature_readings(
    temperatures,
    30
):
    print("High temperature:", temperature)

Output:

High temperature: 31.2
High temperature: 33.4
High temperature: 30.1

The program processes each reading and yields only the values that satisfy the condition.

Later, the same idea could be connected to real sensor data instead of a hard-coded list.

Python Iterators & Generators Quiz

1. Which function creates an iterator from an iterable?



2. Which function requests the next value from an iterator?



3. Which keyword is used to produce values from a generator?



4. What happens when an iterator has no more values?



5. Why can generators be useful with large datasets?





Iterators & Generators Summary

Iterators and generators allow Python to process sequences one item at a time.

You learned how to:

  • Understand iteration.
  • Identify iterables.
  • Create iterators using iter().
  • Retrieve values using next().
  • Understand StopIteration.
  • Understand the iterator protocol.
  • Create custom iterators using classes.
  • Create generators using yield.
  • Understand the difference between return and yield.
  • Create generator expressions.
  • Use generators for large datasets.
  • Build simple data-processing pipelines.
Key idea: An iterator gives you values one at a time, while a generator provides a convenient way to create iterators using yield. Generators are particularly useful when you do not want to load an entire dataset into memory at once.

20. Python Decorators

Decorators are one of Python's most powerful features. They allow you to modify or extend the behavior of a function without changing the function's original code.

That may sound complicated, but the basic idea is surprisingly simple:

A decorator is a function that takes another function, adds some behavior to it, and returns a new function.

Decorators are commonly used in web applications, authentication systems, logging, performance measurement, validation and many other areas of software development.

Before learning decorators, you need to understand two important Python concepts:

  • Functions can be stored in variables.
  • Functions can be passed to other functions.

What Is a Decorator?

Suppose you have a function:

def greet():
    print("Hello!")

Imagine that you want to print a message before and after the function runs.

You could change the function itself:

def greet():
    print("Starting function...")
    print("Hello!")
    print("Function finished.")

But what if you have many functions and want to add the same behavior to all of them?

A decorator can solve this problem without modifying each function directly.

Functions Are Objects

In Python, functions are objects. This means you can assign a function to a variable.

def greet():
    print("Hello!")


message = greet

message()

Output:

Hello!

Both greet and message refer to the same function.

Notice that we used greet without parentheses when assigning it.

message = greet

If you wrote greet(), you would be calling the function immediately instead.

Passing a Function to Another Function

Because functions are objects, you can pass one function into another function.

def greet():
    print("Hello!")


def run_function(function):
    function()


run_function(greet)

Output:

Hello!

The function greet was passed into run_function().

This concept is fundamental to understanding decorators.

Returning a Function

A function can also return another function.

def create_greeting():

    def greet():
        print("Hello!")

    return greet


message = create_greeting()

message()

Output:

Hello!

The inner function was returned by the outer function.

Inner Functions

A function defined inside another function is called an inner function or nested function.

def outer():

    def inner():
        print("Inside the inner function.")

    inner()


outer()

The inner function can be used by the outer function.

Decorators commonly use inner functions because the inner function acts as a wrapper around the original function.

Creating Your First Decorator

Let's build a simple decorator.

def decorator(function):

    def wrapper():
        print("Before the function.")

        function()

        print("After the function.")

    return wrapper

This decorator receives a function, creates a new function called wrapper, and returns the wrapper.

Now let's create a normal function:

def greet():
    print("Hello!")

We can manually decorate it:

greet = decorator(greet)

greet()

Output:

Before the function.
Hello!
After the function.

The original greet() function was wrapped with additional behavior.

The @ Syntax

Python provides a cleaner way to apply decorators using the @ symbol.

Instead of:

def greet():
    print("Hello!")


greet = decorator(greet)

You can write:

@decorator
def greet():
    print("Hello!")

Python effectively applies the decorator to the function.

Now:

greet()

produces:

Before the function.
Hello!
After the function.
Important: The @decorator line is not a comment or special decoration for appearance. It changes how Python creates the function.

Decorators and Function Arguments

What if the function we want to decorate accepts arguments?

def greet(name):
    print("Hello", name)

Our previous wrapper does not accept any arguments.

We can fix this using *args and **kwargs.

def decorator(function):

    def wrapper(*args, **kwargs):

        print("Before function")

        result = function(*args, **kwargs)

        print("After function")

        return result

    return wrapper

Now it can work with functions that receive different numbers and types of arguments.

@decorator
def greet(name):
    print("Hello", name)


greet("Olivia")

Output:

Before function
Hello Olivia
After function

Decorators and Return Values

A decorator should not accidentally remove the result returned by the original function.

Consider:

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

If a wrapper calls this function but does not return the result, the caller may receive None.

A good decorator preserves the result:

def decorator(function):

    def wrapper(*args, **kwargs):

        result = function(*args, **kwargs)

        return result

    return wrapper


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


answer = add(5, 3)

print(answer)

Output:

8

Understanding the Wrapper

The wrapper is the function that receives the call before the original function does.

Think of the structure like this:

decorated function call
        ↓
     wrapper
        ↓
 original function
        ↓
     result
        ↓
     wrapper
        ↓
      caller

The wrapper gives you a place to perform additional actions before or after the original function.

Practical Example: Logging

One common use of decorators is logging.

Suppose you want to know whenever a function is called.

def log_function(function):

    def wrapper(*args, **kwargs):

        print("Function called:", function.__name__)

        result = function(*args, **kwargs)

        return result

    return wrapper


@log_function
def calculate_total(price, quantity):

    return price * quantity


total = calculate_total(500, 3)

print("Total:", total)

Output:

Function called: calculate_total
Total: 1500

This can be useful when debugging larger applications.

Practical Example: Measuring Execution Time

Decorators can also be used to measure how long a function takes to run.

import time


def timer(function):

    def wrapper(*args, **kwargs):

        start = time.perf_counter()

        result = function(*args, **kwargs)

        end = time.perf_counter()

        print(
            function.__name__,
            "took",
            end - start,
            "seconds"
        )

        return result

    return wrapper


@timer
def calculate():

    total = 0

    for number in range(1000000):
        total += number

    return total


calculate()

The exact execution time depends on the computer and what else is running.

The important lesson is that the decorator can measure what happens around the function without changing the function's main calculation.

Practical Example: Access Control

Decorators are also commonly used to control whether a function is allowed to execute.

A simplified example might look like this:

def requires_login(function):

    def wrapper(logged_in):

        if not logged_in:
            print("Please log in first.")
            return

        return function(logged_in)

    return wrapper


@requires_login
def dashboard(logged_in):

    print("Welcome to your dashboard.")


dashboard(False)
dashboard(True)

The decorator checks a condition before allowing the function to continue.

Real authentication systems are more complicated, but this demonstrates the basic principle.

functools.wraps

There is an important improvement you should make when writing reusable decorators.

Python provides functools.wraps to preserve information about the original function.

from functools import wraps


def decorator(function):

    @wraps(function)
    def wrapper(*args, **kwargs):

        return function(*args, **kwargs)

    return wrapper

Without wraps, Python may report information about the wrapper instead of the original function.

When writing decorators that other people may use, functools.wraps is a good practice.

Preserving Function Information

Consider a function with a docstring:

def greet():
    """Say hello to the user."""
    print("Hello!")

When a decorator wraps the function, metadata such as the function's name and documentation can be affected.

Using @wraps(function) helps preserve this information.

from functools import wraps


def decorator(function):

    @wraps(function)
    def wrapper(*args, **kwargs):
        return function(*args, **kwargs)

    return wrapper

Using Multiple Decorators

Python allows you to apply more than one decorator to a function.

@decorator_one
@decorator_two
def greet():
    print("Hello!")

The decorators are applied from the bottom upward.

Conceptually, this is similar to:

greet = decorator_one(
    decorator_two(greet)
)

When using multiple decorators, remember that their order can affect the final behavior.

Decorators That Accept Arguments

Sometimes you want to configure a decorator.

This requires another level of function nesting.

def repeat(times):

    def decorator(function):

        def wrapper(*args, **kwargs):

            for _ in range(times):
                function(*args, **kwargs)

        return wrapper

    return decorator


@repeat(3)
def greet():
    print("Hello!")


greet()

Output:

Hello!
Hello!
Hello!

Notice that repeat(3) first creates the decorator, and that decorator is then applied to greet().

Understanding the Three Layers

Decorators that accept their own arguments can initially look confusing because there are several nested functions.

The structure is:

def outer(decorator_arguments):

    def decorator(function):

        def wrapper(function_arguments):

            # additional behavior

            return function(function_arguments)

        return wrapper

    return decorator

Think of it as:

  • Outer function: receives settings for the decorator.
  • Decorator: receives the function being decorated.
  • Wrapper: receives the function's normal arguments.

Decorators and Classes

Decorators are not limited to ordinary functions. Python also supports decorators for methods and classes.

You have already learned classes and objects. As you progress into larger Python applications, you will encounter decorators such as:

@property
@classmethod
@staticmethod

These are built-in Python decorators.

You do not need to master all of them at once. The important thing is to recognize that decorators are used throughout Python itself.

The @property Decorator

You have already seen that Python classes can contain methods.

The @property decorator allows a method to be accessed like an attribute.

class Person:

    def __init__(self, name):
        self.name = name

    @property
    def description(self):
        return "Person: " + self.name


person = Person("Ada")

print(person.description)

Notice that we use person.description rather than person.description().

This makes the method behave like a calculated attribute.

The @staticmethod Decorator

A static method belongs to a class but does not require access to the instance through self.

class Calculator:

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


print(Calculator.add(5, 3))

Result:

8

The @classmethod Decorator

A class method receives the class itself as its first argument, commonly named cls.

class Student:

    school = "Gabbywall Academy"

    @classmethod
    def show_school(cls):
        return cls.school


print(Student.show_school())

Class methods are useful when the operation relates to the class rather than a particular object.

Decorators in Agricultural Software

Imagine you are building an agricultural monitoring system.

You might have many functions that process sensor readings.

def process_temperature():
    print("Processing temperature...")


def process_moisture():
    print("Processing soil moisture...")


def process_light():
    print("Processing light level...")

Suppose you want to log whenever a sensor-processing function runs.

from functools import wraps


def log_sensor(function):

    @wraps(function)
    def wrapper(*args, **kwargs):

        print("Running sensor:", function.__name__)

        return function(*args, **kwargs)

    return wrapper


@log_sensor
def process_temperature():
    print("Processing temperature...")


@log_sensor
def process_moisture():
    print("Processing soil moisture...")


process_temperature()
process_moisture()

Instead of adding the logging code separately to every function, the decorator provides a reusable solution.

Decorators in Robotics

In robotics software, you may eventually have functions responsible for reading sensors, controlling motors or processing data.

A decorator could be used to log when an operation starts and finishes.

from functools import wraps


def log_operation(function):

    @wraps(function)
    def wrapper(*args, **kwargs):

        print("Starting:", function.__name__)

        result = function(*args, **kwargs)

        print("Finished:", function.__name__)

        return result

    return wrapper


@log_operation
def read_sensor():
    print("Reading sensor data.")


read_sensor()

As your robotics programs become larger, reusable patterns such as this can help keep your code organized.

When Should You Use Decorators?

Decorators are useful when you want to apply the same behavior to multiple functions without duplicating code.

Common examples include:

  • Logging
  • Authentication
  • Authorization
  • Performance measurement
  • Input validation
  • Caching
  • Error handling
  • Access control

The key question to ask is:

"Do I need to add the same surrounding behavior to several functions?"

If the answer is yes, a decorator may be a good solution.

Common Decorator Mistakes

1. Forgetting to return the wrapper

def decorator(function):

    def wrapper():
        function()

    # Missing:
    # return wrapper

Without returning the wrapper, the decorated function may no longer behave as expected.

2. Forgetting function arguments

If the original function accepts arguments, the wrapper should generally use *args and **kwargs when the decorator needs to support arbitrary functions.

3. Forgetting the return value

If the original function returns something, the wrapper should normally return that result.

4. Forgetting functools.wraps

For reusable decorators, @wraps helps preserve the original function's metadata.

5. Using decorators when they make the code harder to understand

Decorators are powerful, but they are not required everywhere. Simple code is often better when a decorator does not provide a clear benefit.

Practice Exercises

Exercise 1: Basic Decorator

Create a decorator that prints "Starting..." before a function runs.

Exercise 2: Before and After

Create a decorator that prints a message before and after the decorated function executes.

Exercise 3: Arguments

Create a decorator that works with a function accepting two arguments.

Exercise 4: Return Values

Create a decorator for an addition function and make sure the result is still returned correctly.

Exercise 5: Logging

Create a decorator that prints the name of the function being called.

Exercise 6: Timing

Create a decorator that measures approximately how long a function takes to execute.

Exercise 7: Authentication

Create a decorator that allows a function to execute only when a user is logged in.

Mini Project: Function Logger

Let's create a reusable function logger.

from functools import wraps


def logger(function):

    @wraps(function)
    def wrapper(*args, **kwargs):

        print("Calling:", function.__name__)

        result = function(*args, **kwargs)

        print("Result:", result)

        return result

    return wrapper


@logger
def multiply(a, b):

    return a * b


answer = multiply(6, 7)

print("Final answer:", answer)

Output will look similar to:

Calling: multiply
Result: 42
Final answer: 42

This small project demonstrates the core decorator pattern:

  1. Receive the original function.
  2. Create a wrapper.
  3. Perform additional behavior.
  4. Call the original function.
  5. Return its result.

Python Decorators Quiz

1. What is a decorator?



2. Which symbol is commonly used to apply a decorator?



3. Why are *args and **kwargs commonly used in wrappers?



4. What does functools.wraps help preserve?



5. Which is a common use of decorators?





Python Decorators Summary

Decorators allow you to add or modify behavior around existing functions without changing their original code.

You learned how to:

  • Understand functions as objects.
  • Pass functions to other functions.
  • Return functions from functions.
  • Create inner functions.
  • Build a basic decorator.
  • Use the @ decorator syntax.
  • Handle function arguments with *args and **kwargs.
  • Preserve function return values.
  • Use functools.wraps.
  • Create logging decorators.
  • Measure function execution time.
  • Create simple access-control decorators.
  • Use multiple decorators.
  • Create configurable decorators.
  • Recognize built-in decorators such as @property, @staticmethod and @classmethod.
Key idea: A decorator is essentially a reusable way of saying, "Before or after this function runs, I want to do something else." Once you understand functions, wrappers and @ syntax, decorators become much easier to reason about.

21. Python Environments & Packages

So far, you have mostly worked with Python's built-in features and standard library. But real-world Python development often requires additional libraries.

For example, you might eventually want to use libraries for:

  • Data analysis
  • Artificial intelligence
  • Machine learning
  • Computer vision
  • Web development
  • Robotics
  • Scientific computing
  • Working with databases

Python has a huge ecosystem of packages that provide functionality you don't have to build yourself.

Big idea: Python packages allow you to use existing, tested functionality instead of writing everything from scratch.

What Is a Python Package?

A package is a collection of Python code that can be installed and reused in your projects.

For example, suppose you want to perform advanced numerical calculations. Instead of building every mathematical operation yourself, you can use a package such as NumPy.

Another example is OpenCV, which provides tools for computer vision and image processing.

A package can contain:

  • Functions
  • Classes
  • Modules
  • Data
  • Other supporting files

Library vs Package

You will often hear the words library and package used in Python.

They are related, but they are not always technically identical.

A package is a particular way of organizing and distributing Python code. The word library is often used more generally to describe reusable functionality.

In everyday Python discussions, people may use the terms interchangeably. Don't worry too much about the distinction at this stage.

What Is pip?

pip is the standard package installer commonly used with Python.

It allows you to install packages from the Python Package Index and other package sources.

For example:

pip install requests

This tells pip to install the requests package.

Depending on your computer and Python installation, you may instead use:

python -m pip install requests

On some systems, especially when multiple Python versions are installed, you may use:

python3 -m pip install requests
Tip: Using python -m pip helps make it clear which Python installation is being used to run pip.

Installing a Package

Let's install a package called requests.

Open your terminal and run:

python -m pip install requests

pip will download the package and install it into the Python environment you are currently using.

After installation, you can import it into your program.

import requests

print(requests.__version__)

If the package is installed correctly, Python will be able to import it.

Uninstalling a Package

You can remove an installed package using:

python -m pip uninstall requests

pip will normally ask you to confirm the removal.

If you no longer need a package, uninstalling it can help keep an environment clean.

Viewing Installed Packages

To see packages installed in your current environment, use:

python -m pip list

You will see information such as package names and installed versions.

This is useful when troubleshooting projects.

Viewing Package Information

You can inspect information about a particular package with:

python -m pip show requests

This can display information such as the installed version and installation location.

Why Package Versions Matter

Imagine that you build a Python application today and install version 2 of a package.

Six months later, someone installs the same project but receives version 3 of that package.

If the newer version changed something important, your program might stop working.

This is why Python projects commonly record their dependencies and their versions.

What Is a Virtual Environment?

A virtual environment is an isolated Python environment created for a particular project.

This allows different projects to use different package versions without interfering with each other.

Imagine you have two projects:

  • Project A requires one version of a package.
  • Project B requires another version.

Installing everything globally can create conflicts.

Virtual environments solve this by giving each project its own isolated collection of installed packages.

Think of a virtual environment as a private Python workspace for one project.

Creating a Virtual Environment

First create a folder for your project:

mkdir my_project

Move into the folder:

cd my_project

Then create a virtual environment:

python -m venv .venv

The .venv directory will contain the environment.

The name .venv is a common convention, but you could use another name.

Activating a Virtual Environment on macOS or Linux

On macOS or Linux, use:

source .venv/bin/activate

Once activated, your terminal will usually show the environment name near the beginning of the command prompt.

You can then install packages and work on the project.

Activating a Virtual Environment on Windows

In Windows Command Prompt:

.venv\Scripts\activate

In PowerShell:

.venv\Scripts\Activate.ps1

The exact command can depend on your shell configuration.

Deactivating a Virtual Environment

When you are finished working with the environment, run:

deactivate

Your terminal will return to the normal Python environment.

Checking Which Python You Are Using

When working with virtual environments, it is useful to know which Python executable is active.

On macOS or Linux:

which python

On Windows:

where python

You can also ask Python directly where it is installed:

import sys

print(sys.executable)

This is particularly useful when you think a package has been installed but Python cannot find it.

Installing Packages Inside a Virtual Environment

Activate your virtual environment first.

source .venv/bin/activate

Then install your package:

python -m pip install requests

The package will be installed into that environment rather than your system-wide Python environment.

requirements.txt

A Python project may depend on several external packages.

Instead of telling someone to install each package manually, you can create a file called requirements.txt.

For example:

requests==2.32.3
numpy==2.1.0

The file records packages that the project needs.

You can then install them with:

python -m pip install -r requirements.txt

This is especially useful when sharing projects with other developers.

Generating requirements.txt with pip freeze

You can use pip freeze to display installed packages and their versions.

python -m pip freeze

You can save the result to a requirements file:

python -m pip freeze > requirements.txt

This creates a snapshot of the packages installed in the current environment.

Important: pip freeze records installed packages. For larger projects, you should still think carefully about which dependencies your application actually needs.

Updating a Package

You can upgrade a package using:

python -m pip install --upgrade requests

Be careful when upgrading dependencies in an existing project. A newer version can sometimes introduce changes that require code modifications.

What Is PyPI?

PyPI stands for the Python Package Index.

It is a major repository for Python packages.

When you run a command such as:

python -m pip install requests

pip can retrieve the package from PyPI.

Before installing a package, however, you should consider whether it is trustworthy and whether it is actually necessary for your project.

Importing Third-Party Packages

Once a package has been installed, you can normally import it just like other Python modules.

For example:

import requests

You can then use the functionality provided by the package.

Installation and importing are two different steps:

Installation
    ↓
pip install package

Import
    ↓
import package

Installing a package does not automatically import it into every Python program.

ModuleNotFoundError

One common problem beginners encounter is:

ModuleNotFoundError

For example:

import requests

If the package is not available in the Python environment running your program, Python may produce an error similar to:

ModuleNotFoundError: No module named 'requests'

A common solution is to install the package into the correct environment:

python -m pip install requests

If the error continues, check which Python executable is running your program.

Virtual Environments and Your IDE

Modern Python IDEs can usually detect virtual environments.

However, sometimes your editor may be using a different Python interpreter from the one where you installed your package.

This can create a confusing situation:

"I installed the package, but Python says it doesn't exist."

In many cases, the problem is not the package itself. The IDE and terminal are using different Python environments.

Environment Variables

Some applications need configuration values such as API keys, database credentials or application settings.

These values should generally not be hard-coded directly into your source code.

For example, avoid writing:

API_KEY = "my-secret-key"

in a project that will be shared publicly.

Instead, applications often use environment variables or a secure secrets system.

You will encounter this concept frequently when working with APIs, web applications and cloud services.

.env Files and python-dotenv

A popular approach in local development is to store configuration values in a .env file.

A simplified example might look like:

API_KEY=my-secret-key

A package such as python-dotenv can load these values into the application's environment.

python -m pip install python-dotenv

Then:

from dotenv import load_dotenv
import os

load_dotenv()

api_key = os.getenv("API_KEY")

print(api_key)
Security: Never publish real API keys, passwords or other secrets in a public repository. Also make sure sensitive .env files are excluded from version control when appropriate.

.gitignore

When using Git, you can create a .gitignore file to tell Git which files should not be tracked.

A Python project might include entries such as:

.venv/
__pycache__/
.env

This helps prevent virtual-environment files, Python cache files and local secrets from accidentally being committed.

Basic Python Project Structure

A simple project might eventually look like this:

my_project/
│
├── .venv/
├── .env
├── .gitignore
├── requirements.txt
├── main.py
└── utils.py

You do not need every file in every project. The structure depends on what you are building.

The important thing is to understand that a project can contain both your own code and external dependencies.

Standard Library vs Third-Party Packages

Python comes with a large standard library.

For example:

import math
import random
import datetime
import os
import json

These modules are generally available as part of Python itself.

Third-party packages are installed separately.

For example:

import numpy
import requests
import cv2

The exact package you need depends on your project.

Example: NumPy

NumPy is widely used for numerical and scientific computing in Python.

Install it with:

python -m pip install numpy

Then:

import numpy as np

numbers = np.array([10, 20, 30, 40])

print(numbers)

The as np syntax gives the imported module a shorter alias.

Example: OpenCV

OpenCV is a computer vision library.

This becomes particularly interesting if you want to work with images, cameras or object detection.

The package is commonly installed using:

python -m pip install opencv-python

But the import name is:

import cv2

Notice that the package installation name and the import name do not always have to be identical.

Packages and Robotics

Packages become extremely important when you begin building robotics applications.

Depending on your hardware and project, you may encounter packages for:

  • GPIO control
  • Serial communication
  • Computer vision
  • Numerical calculations
  • Sensor data processing
  • Machine learning
  • Robotics frameworks

For example, a future agricultural robot could use Python packages to process camera images, analyze sensor readings and communicate with other components.

Packages and Agricultural AI

If you eventually build a crop-and-weed detection system, you will likely work with several specialized libraries.

A project could potentially involve tools for:

  • Numerical arrays
  • Image processing
  • Data preparation
  • Machine learning
  • Deep learning
  • Visualization

This is one reason understanding environments and package management is important before you move into advanced AI development.

Common Mistakes

1. Installing packages globally for every project

This can eventually create dependency conflicts. Virtual environments are usually a better approach for project-specific dependencies.

2. Installing the package into the wrong Python

You may have multiple Python installations. Using python -m pip can help ensure pip is connected to the Python interpreter you intend to use.

3. Forgetting to activate the environment

If you intended to install a package into a virtual environment but the environment wasn't active, the package may have been installed somewhere else.

4. Committing .env files

Never casually commit files containing real secrets.

5. Installing packages without understanding what they do

Don't install a package simply because you saw it in someone else's code. Understand why your project needs it.

6. Installing unnecessary packages

More dependencies can mean more complexity. Install what your project actually requires.

Practice Exercises

Exercise 1: Create an Environment

Create a new folder called python_practice and create a virtual environment named .venv.

Exercise 2: Activate It

Activate the environment using the command appropriate for your operating system.

Exercise 3: Install a Package

Install the requests package.

Exercise 4: Check Your Packages

Use pip to display the packages installed in your environment.

Exercise 5: Create requirements.txt

Generate a requirements.txt file containing your environment's installed packages.

Exercise 6: Check Your Python

Write a Python program that prints the location of the Python executable currently running your program.

Exercise 7: Project Structure

Create a small Python project containing:

  • main.py
  • utils.py
  • requirements.txt
  • .gitignore

Mini Project: A Clean Python Project

Let's combine the concepts from this module into a small project structure.

student_app/
│
├── .venv/
├── .gitignore
├── requirements.txt
├── main.py
└── utils.py

In utils.py:

def calculate_average(scores):

    if not scores:
        return 0

    return sum(scores) / len(scores)

In main.py:

from utils import calculate_average


scores = [80, 75, 90, 85]

average = calculate_average(scores)

print("Average:", average)

This project does not require an external package, and that is an important lesson too: you should not install a package when Python's built-in features are enough.

Python Environments & Packages Quiz

1. What is pip primarily used for?



2. What is a virtual environment?



3. Which command creates a virtual environment named .venv?



4. What is requirements.txt commonly used for?



5. Which command installs packages listed in requirements.txt?



6. Why should you be careful with .env files?





Python Environments & Packages Summary

Python's ecosystem contains thousands of reusable packages. Understanding how to install and manage them is an essential skill for real-world development.

You learned:

  • What Python packages are.
  • What pip does.
  • How to install and uninstall packages.
  • How to inspect installed packages.
  • Why package versions matter.
  • What virtual environments are.
  • How to create and activate a virtual environment.
  • How to deactivate an environment.
  • How to create and use requirements.txt.
  • How to use pip freeze.
  • How third-party packages are imported.
  • How to troubleshoot ModuleNotFoundError.
  • Why IDEs sometimes use the wrong interpreter.
  • What environment variables are.
  • Why secrets should not be hard-coded or publicly committed.
  • How packages can support robotics and agricultural AI projects.
Key idea: A professional Python project should have a controlled environment and a clear record of the external packages it depends on.

22. Python Projects

Congratulations! You have reached the project section of the Python course.

You have learned variables, data types, operators, conditions, loops, strings, lists, tuples, sets, dictionaries, functions, modules, file handling, exceptions, classes, dates, regular expressions, iterators, generators, decorators and package management.

Now it is time to combine those skills.

Remember: You do not become good at programming by only reading code. You become better by writing, testing, breaking and fixing programs.

How to Learn From These Projects

Don't immediately copy the complete solution.

First read the problem and try to design your own solution.

A useful process is:

  1. Understand the problem.
  2. Break it into smaller tasks.
  3. Write pseudocode.
  4. Write the Python code.
  5. Run the program.
  6. Find and fix errors.
  7. Improve the program.

If you get stuck, look at the hints before looking at a complete solution.

Project 1: Calculator

Our first project is a simple calculator that allows the user to perform basic arithmetic operations.

What You Will Practice

  • Variables
  • Input
  • Conditions
  • Functions
  • Operators

Requirements

Your calculator should allow the user to:

  • Add two numbers
  • Subtract two numbers
  • Multiply two numbers
  • Divide two numbers

Starter Version

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


def subtract(a, b):
    return a - b


def multiply(a, b):
    return a * b


def divide(a, b):
    if b == 0:
        return "Cannot divide by zero."

    return a / b


print("Python Calculator")

first = float(input("Enter first number: "))
operator = input("Enter operator (+, -, *, /): ")
second = float(input("Enter second number: "))

if operator == "+":
    print(add(first, second))

elif operator == "-":
    print(subtract(first, second))

elif operator == "*":
    print(multiply(first, second))

elif operator == "/":
    print(divide(first, second))

else:
    print("Invalid operator.")

Challenge

Improve the calculator so that it continues running until the user chooses to exit.

You can also add exponentiation using ** and a remainder operation using %.

Project 2: Number Guessing Game

In this project, the computer chooses a secret number and the player tries to guess it.

What You Will Practice

  • Variables
  • Input
  • Conditions
  • while loops
  • Random numbers
  • Counters

Basic Version

import random

secret_number = random.randint(1, 100)

attempts = 0

print("Guess the number between 1 and 100.")

while True:

    guess = int(input("Enter your guess: "))

    attempts += 1

    if guess < secret_number:
        print("Too low.")

    elif guess > secret_number:
        print("Too high.")

    else:
        print("Correct!")
        print("Attempts:", attempts)
        break

Challenge

Improve the game by:

  • Limiting the player to a certain number of attempts.
  • Adding difficulty levels.
  • Giving the player another round.
  • Keeping track of the best score.

Project 3: Currency Converter

This project is particularly useful for practicing input, calculations, dictionaries and functions.

To keep the project simple, we'll use fixed example exchange rates.

Important: Real exchange rates change. A production currency converter should obtain current rates from a reliable exchange-rate service rather than relying permanently on hard-coded values.

Example

rates = {
    "USD": 1,
    "EUR": 0.92,
    "GBP": 0.79,
    "NGN": 1500
}


def convert(amount, from_currency, to_currency):

    usd_amount = amount / rates[from_currency]

    result = usd_amount * rates[to_currency]

    return result


print("Currency Converter")

amount = float(input("Enter amount: "))

from_currency = input(
    "From currency (USD/EUR/GBP/NGN): "
).upper()

to_currency = input(
    "To currency (USD/EUR/GBP/NGN): "
).upper()

if from_currency in rates and to_currency in rates:

    result = convert(
        amount,
        from_currency,
        to_currency
    )

    print(
        f"{amount:.2f} {from_currency} = "
        f"{result:.2f} {to_currency}"
    )

else:
    print("Unsupported currency.")

Challenge

Add more currencies and allow the user to perform multiple conversions without restarting the program.

Project 4: Student Score Calculator

Now let's build something similar to the type of program you may encounter while learning Python: a student score analyzer.

Requirements

The program should collect scores and calculate:

  • Total
  • Average
  • Highest score
  • Lowest score

Example

scores = []

for subject in ["Biology", "Chemistry", "English", "Physics"]:

    score = float(
        input(f"Enter {subject} score: ")
    )

    scores.append(score)


total = sum(scores)
average = total / len(scores)
highest = max(scores)
lowest = min(scores)

print("Total:", total)
print("Average:", average)
print("Highest:", highest)
print("Lowest:", lowest)

Challenge

Turn the program into a menu-driven application.

For example:

1. Enter scores
2. Show total
3. Show average
4. Show highest score
5. Show lowest score
6. Exit

This will give you practice combining loops, functions, conditions and lists.

Project 5: To-Do List

Now we are going to build something closer to a small application.

A to-do list allows users to add, view and remove tasks.

What You Will Practice

  • Lists
  • Functions
  • while loops
  • Conditions
  • User input

Example

tasks = []


def show_tasks():

    if not tasks:
        print("No tasks yet.")
        return

    for number, task in enumerate(tasks, start=1):
        print(number, task)


while True:

    print("\n1. Add task")
    print("2. View tasks")
    print("3. Remove task")
    print("4. Exit")

    choice = input("Choose an option: ")

    if choice == "1":

        task = input("Enter task: ")
        tasks.append(task)

        print("Task added.")

    elif choice == "2":

        show_tasks()

    elif choice == "3":

        show_tasks()

        if tasks:

            number = int(
                input("Enter task number: ")
            )

            if 1 <= number <= len(tasks):
                removed = tasks.pop(number - 1)
                print("Removed:", removed)
            else:
                print("Invalid task number.")

    elif choice == "4":

        print("Goodbye!")
        break

    else:

        print("Invalid option.")

Challenge

Add the ability to mark tasks as completed.

Then improve the project by saving tasks to a file so they are still available after the program closes.

Project 6: Quiz Application

A quiz application is an excellent project for combining dictionaries, lists, loops, conditions and functions.

Example

questions = [
    {
        "question": "What keyword creates a function?",
        "answer": "def"
    },
    {
        "question": "What data type stores True or False?",
        "answer": "bool"
    },
    {
        "question": "What keyword creates a loop that repeats while a condition is true?",
        "answer": "while"
    }
]


score = 0


for item in questions:

    print(item["question"])

    answer = input("Your answer: ").strip().lower()

    if answer == item["answer"].lower():

        print("Correct!")
        score += 1

    else:

        print("Incorrect.")

print(
    f"You scored {score} out of {len(questions)}."
)

Challenge

Add multiple-choice questions.

Then add:

  • Different categories
  • Difficulty levels
  • A timer
  • High scores
  • Questions loaded from a JSON file

Project 7: Contact Book

Build a program that stores people's names and contact information.

A dictionary is a natural structure for this type of application.

Requirements

  • Add a contact
  • View contacts
  • Search for a contact
  • Update a contact
  • Delete a contact

Starting Structure

contacts = {
    "Ada": {
        "phone": "08000000000",
        "email": "ada@example.com"
    }
}

Your challenge is to build the menu and functions around this data.

Project 8: Text File Analyzer

This project combines file handling with strings and basic statistics.

The program should open a text file and calculate:

  • Number of characters
  • Number of words
  • Number of lines

Example

with open(
    "notes.txt",
    "r",
    encoding="utf-8"
) as file:

    content = file.read()


characters = len(content)

words = content.split()

lines = content.splitlines()


print("Characters:", characters)
print("Words:", len(words))
print("Lines:", len(lines))

Challenge

Add a word-frequency counter that shows how many times each word appears.

Final Python Project

Now it is time to build something larger.

Your final project should combine several concepts from this course rather than testing only one Python feature.

Recommended Final Project: Agricultural Field Monitor

Since Python can be used for agriculture, robotics and AI, an excellent project is a simple agricultural field monitoring application.

The first version does not need artificial intelligence or physical hardware.

Start with a software simulation.

Project Idea

Create a Python application that stores information about agricultural plots and monitors simulated environmental readings.

The application could record:

  • Field name
  • Crop planted
  • Temperature
  • Soil moisture
  • Humidity
  • Plant growth observations

Example Data

field = {
    "name": "North Field",
    "crop": "Maize",
    "temperature": 29.5,
    "soil_moisture": 42,
    "humidity": 70
}

Possible Rules

Your program could check whether the soil moisture is too low.

if field["soil_moisture"] < 30:

    print("Warning: Soil may need irrigation.")

else:

    print("Soil moisture is acceptable.")

Turn It Into an Application

Add a menu such as:

================================
AGRICULTURAL FIELD MONITOR
================================

1. Add field
2. View fields
3. Record sensor reading
4. Check field status
5. View reports
6. Save data
7. Exit

Concepts You Can Use

  • Variables
  • Data types
  • Conditions
  • Loops
  • Lists
  • Dictionaries
  • Functions
  • Modules
  • File handling
  • JSON
  • Exception handling
  • Classes

This is much closer to how real software is developed: several Python concepts working together rather than one isolated feature.

Taking the Project Further With Classes

Once your procedural version works, you can redesign it using classes.

class Field:

    def __init__(
        self,
        name,
        crop,
        temperature,
        soil_moisture,
        humidity
    ):

        self.name = name
        self.crop = crop
        self.temperature = temperature
        self.soil_moisture = soil_moisture
        self.humidity = humidity


    def check_status(self):

        if self.soil_moisture < 30:
            return "Needs irrigation"

        return "Healthy"


field = Field(
    "North Field",
    "Maize",
    29.5,
    42,
    70
)


print(field.name)
print(field.check_status())

Now your project is using object-oriented programming as well.

Saving the Project Data

You can save field information using JSON.

import json


field = {
    "name": "North Field",
    "crop": "Maize",
    "temperature": 29.5,
    "soil_moisture": 42,
    "humidity": 70
}


with open(
    "field.json",
    "w",
    encoding="utf-8"
) as file:

    json.dump(field, file, indent=4)

Later, your program can load the data again.

with open(
    "field.json",
    "r",
    encoding="utf-8"
) as file:

    field = json.load(file)

print(field["crop"])

Taking the Project to the Next Level

Once the software simulation works, you can gradually make the project more realistic.

For example:

  1. Use real sensor data.
  2. Connect an Arduino or Raspberry Pi.
  3. Store readings over time.
  4. Display the data in charts.
  5. Add a camera.
  6. Process images with OpenCV.
  7. Train an object-detection model.
  8. Detect crops or weeds.
  9. Connect the system to an agricultural robot.

Notice the progression:

Python basics
      ↓
Python application
      ↓
File/data storage
      ↓
Sensors
      ↓
Computer vision
      ↓
AI
      ↓
Robotics

This is how a simple programming project can eventually become the foundation for a much larger engineering system.

Good Project Habits

As your programs become larger, start developing professional habits.

1. Use Functions

Don't put your entire program inside one enormous block of code.

2. Use Meaningful Names

student_scores = [80, 90, 75]

is easier to understand than:

x = [80, 90, 75]

3. Handle Errors

Assume users will enter unexpected information.

4. Save Data Carefully

If your application stores important information, make sure data is not accidentally overwritten or lost.

5. Test Small Pieces

Test individual functions before combining everything.

6. Use Version Control

Git can help you track changes and return to earlier versions of your project.

7. Read Error Messages

An error message is not simply Python telling you that you failed. It is information about what Python encountered.

Final Challenge

Build your own Python application without following a complete tutorial.

Choose a problem you care about and design a solution.

For example:

  • Expense tracker
  • Inventory manager
  • Study planner
  • Weather information tool
  • Farm record manager
  • Plant observation tracker
  • Sensor monitoring simulator
  • Simple robotics control simulator
Your goal is not to write the perfect program. Your goal is to take a problem, break it down and build a working solution.

You Have Completed the Python Course

You have now moved through the major foundations of Python.

More importantly, you have reached the point where you can begin building your own programs instead of only following examples.

You Can Now Work With:

  • Variables and data types
  • Operators
  • Conditions
  • Loops
  • Strings
  • Lists
  • Tuples
  • Sets
  • Dictionaries
  • Functions
  • Modules
  • Files
  • Exceptions
  • Classes and objects
  • Dates and times
  • Regular expressions
  • Iterators and generators
  • Decorators
  • Packages and virtual environments

But completing a Python course does not mean you are finished learning Python.

It means you now have a foundation from which you can explore more specialized areas.

What Should You Learn Next?

Your next step should depend on what you want to build.

For Web Development

Explore frameworks such as Flask or Django.

For Data Science

Explore NumPy, pandas, Matplotlib and related tools.

For Artificial Intelligence

Learn NumPy, data processing, machine learning and deep learning.

For Computer Vision

Learn OpenCV and image processing before moving deeper into object detection.

For Robotics

Continue with Python while learning electronics, sensors, control systems, embedded programming and robotics frameworks.

For Agricultural Technology

Combine programming with agriculture, sensors, computer vision, data analysis and eventually robotics.

Don't try to learn everything at once. Choose a direction, build projects and learn the tools required to solve the problems in front of you.

Python Projects Quiz

1. What is the best way to improve your programming skills?



2. Which data structure is useful for storing key-value information?



3. Which module can generate random numbers?



4. Why are functions useful in larger projects?



5. Which format can be used to store structured data?



6. What should you do when you encounter an error?





Python Projects Summary

Projects are where your Python knowledge starts becoming practical.

In this module you worked through projects involving:

  • Calculators
  • Games
  • Currency conversion
  • Student score analysis
  • To-do lists
  • Quiz applications
  • Contact books
  • File analysis
  • Agricultural field monitoring

You also learned how a simple Python application can gradually evolve into a larger system involving files, sensors, computer vision, AI and robotics.

Final lesson: Don't measure your progress by how much Python syntax you can memorize. Measure it by how effectively you can use Python to solve problems.

22. Python Projects

Congratulations! You have reached the project section of the Python course.

You have learned variables, data types, operators, conditions, loops, strings, lists, tuples, sets, dictionaries, functions, modules, file handling, exceptions, classes, dates, regular expressions, iterators, generators, decorators and package management.

Now it is time to combine those skills.

Remember: You do not become good at programming by only reading code. You become better by writing, testing, breaking and fixing programs.

How to Learn From These Projects

Don't immediately copy the complete solution.

First read the problem and try to design your own solution.

A useful process is:

  1. Understand the problem.
  2. Break it into smaller tasks.
  3. Write pseudocode.
  4. Write the Python code.
  5. Run the program.
  6. Find and fix errors.
  7. Improve the program.

If you get stuck, look at the hints before looking at a complete solution.

Project 1: Calculator

Our first project is a simple calculator that allows the user to perform basic arithmetic operations.

What You Will Practice

  • Variables
  • Input
  • Conditions
  • Functions
  • Operators

Requirements

Your calculator should allow the user to:

  • Add two numbers
  • Subtract two numbers
  • Multiply two numbers
  • Divide two numbers

Starter Version

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


def subtract(a, b):
    return a - b


def multiply(a, b):
    return a * b


def divide(a, b):
    if b == 0:
        return "Cannot divide by zero."

    return a / b


print("Python Calculator")

first = float(input("Enter first number: "))
operator = input("Enter operator (+, -, *, /): ")
second = float(input("Enter second number: "))

if operator == "+":
    print(add(first, second))

elif operator == "-":
    print(subtract(first, second))

elif operator == "*":
    print(multiply(first, second))

elif operator == "/":
    print(divide(first, second))

else:
    print("Invalid operator.")

Challenge

Improve the calculator so that it continues running until the user chooses to exit.

You can also add exponentiation using ** and a remainder operation using %.

Project 2: Number Guessing Game

In this project, the computer chooses a secret number and the player tries to guess it.

What You Will Practice

  • Variables
  • Input
  • Conditions
  • while loops
  • Random numbers
  • Counters

Basic Version

import random

secret_number = random.randint(1, 100)

attempts = 0

print("Guess the number between 1 and 100.")

while True:

    guess = int(input("Enter your guess: "))

    attempts += 1

    if guess < secret_number:
        print("Too low.")

    elif guess > secret_number:
        print("Too high.")

    else:
        print("Correct!")
        print("Attempts:", attempts)
        break

Challenge

Improve the game by:

  • Limiting the player to a certain number of attempts.
  • Adding difficulty levels.
  • Giving the player another round.
  • Keeping track of the best score.

Project 3: Currency Converter

This project is particularly useful for practicing input, calculations, dictionaries and functions.

To keep the project simple, we'll use fixed example exchange rates.

Important: Real exchange rates change. A production currency converter should obtain current rates from a reliable exchange-rate service rather than relying permanently on hard-coded values.

Example

rates = {
    "USD": 1,
    "EUR": 0.92,
    "GBP": 0.79,
    "NGN": 1500
}


def convert(amount, from_currency, to_currency):

    usd_amount = amount / rates[from_currency]

    result = usd_amount * rates[to_currency]

    return result


print("Currency Converter")

amount = float(input("Enter amount: "))

from_currency = input(
    "From currency (USD/EUR/GBP/NGN): "
).upper()

to_currency = input(
    "To currency (USD/EUR/GBP/NGN): "
).upper()

if from_currency in rates and to_currency in rates:

    result = convert(
        amount,
        from_currency,
        to_currency
    )

    print(
        f"{amount:.2f} {from_currency} = "
        f"{result:.2f} {to_currency}"
    )

else:
    print("Unsupported currency.")

Challenge

Add more currencies and allow the user to perform multiple conversions without restarting the program.

Project 4: Student Score Calculator

Now let's build something similar to the type of program you may encounter while learning Python: a student score analyzer.

Requirements

The program should collect scores and calculate:

  • Total
  • Average
  • Highest score
  • Lowest score

Example

scores = []

for subject in ["Biology", "Chemistry", "English", "Physics"]:

    score = float(
        input(f"Enter {subject} score: ")
    )

    scores.append(score)


total = sum(scores)
average = total / len(scores)
highest = max(scores)
lowest = min(scores)

print("Total:", total)
print("Average:", average)
print("Highest:", highest)
print("Lowest:", lowest)

Challenge

Turn the program into a menu-driven application.

For example:

1. Enter scores
2. Show total
3. Show average
4. Show highest score
5. Show lowest score
6. Exit

This will give you practice combining loops, functions, conditions and lists.

Project 5: To-Do List

Now we are going to build something closer to a small application.

A to-do list allows users to add, view and remove tasks.

What You Will Practice

  • Lists
  • Functions
  • while loops
  • Conditions
  • User input

Example

tasks = []


def show_tasks():

    if not tasks:
        print("No tasks yet.")
        return

    for number, task in enumerate(tasks, start=1):
        print(number, task)


while True:

    print("\n1. Add task")
    print("2. View tasks")
    print("3. Remove task")
    print("4. Exit")

    choice = input("Choose an option: ")

    if choice == "1":

        task = input("Enter task: ")
        tasks.append(task)

        print("Task added.")

    elif choice == "2":

        show_tasks()

    elif choice == "3":

        show_tasks()

        if tasks:

            number = int(
                input("Enter task number: ")
            )

            if 1 <= number <= len(tasks):
                removed = tasks.pop(number - 1)
                print("Removed:", removed)
            else:
                print("Invalid task number.")

    elif choice == "4":

        print("Goodbye!")
        break

    else:

        print("Invalid option.")

Challenge

Add the ability to mark tasks as completed.

Then improve the project by saving tasks to a file so they are still available after the program closes.

Project 6: Quiz Application

A quiz application is an excellent project for combining dictionaries, lists, loops, conditions and functions.

Example

questions = [
    {
        "question": "What keyword creates a function?",
        "answer": "def"
    },
    {
        "question": "What data type stores True or False?",
        "answer": "bool"
    },
    {
        "question": "What keyword creates a loop that repeats while a condition is true?",
        "answer": "while"
    }
]


score = 0


for item in questions:

    print(item["question"])

    answer = input("Your answer: ").strip().lower()

    if answer == item["answer"].lower():

        print("Correct!")
        score += 1

    else:

        print("Incorrect.")

print(
    f"You scored {score} out of {len(questions)}."
)

Challenge

Add multiple-choice questions.

Then add:

  • Different categories
  • Difficulty levels
  • A timer
  • High scores
  • Questions loaded from a JSON file

Project 7: Contact Book

Build a program that stores people's names and contact information.

A dictionary is a natural structure for this type of application.

Requirements

  • Add a contact
  • View contacts
  • Search for a contact
  • Update a contact
  • Delete a contact

Starting Structure

contacts = {
    "Ada": {
        "phone": "08000000000",
        "email": "ada@example.com"
    }
}

Your challenge is to build the menu and functions around this data.

Project 8: Text File Analyzer

This project combines file handling with strings and basic statistics.

The program should open a text file and calculate:

  • Number of characters
  • Number of words
  • Number of lines

Example

with open(
    "notes.txt",
    "r",
    encoding="utf-8"
) as file:

    content = file.read()


characters = len(content)

words = content.split()

lines = content.splitlines()


print("Characters:", characters)
print("Words:", len(words))
print("Lines:", len(lines))

Challenge

Add a word-frequency counter that shows how many times each word appears.

Final Python Project

Now it is time to build something larger.

Your final project should combine several concepts from this course rather than testing only one Python feature.

Recommended Final Project: Agricultural Field Monitor

Since Python can be used for agriculture, robotics and AI, an excellent project is a simple agricultural field monitoring application.

The first version does not need artificial intelligence or physical hardware.

Start with a software simulation.

Project Idea

Create a Python application that stores information about agricultural plots and monitors simulated environmental readings.

The application could record:

  • Field name
  • Crop planted
  • Temperature
  • Soil moisture
  • Humidity
  • Plant growth observations

Example Data

field = {
    "name": "North Field",
    "crop": "Maize",
    "temperature": 29.5,
    "soil_moisture": 42,
    "humidity": 70
}

Possible Rules

Your program could check whether the soil moisture is too low.

if field["soil_moisture"] < 30:

    print("Warning: Soil may need irrigation.")

else:

    print("Soil moisture is acceptable.")

Turn It Into an Application

Add a menu such as:

================================
AGRICULTURAL FIELD MONITOR
================================

1. Add field
2. View fields
3. Record sensor reading
4. Check field status
5. View reports
6. Save data
7. Exit

Concepts You Can Use

  • Variables
  • Data types
  • Conditions
  • Loops
  • Lists
  • Dictionaries
  • Functions
  • Modules
  • File handling
  • JSON
  • Exception handling
  • Classes

This is much closer to how real software is developed: several Python concepts working together rather than one isolated feature.

Taking the Project Further With Classes

Once your procedural version works, you can redesign it using classes.

class Field:

    def __init__(
        self,
        name,
        crop,
        temperature,
        soil_moisture,
        humidity
    ):

        self.name = name
        self.crop = crop
        self.temperature = temperature
        self.soil_moisture = soil_moisture
        self.humidity = humidity


    def check_status(self):

        if self.soil_moisture < 30:
            return "Needs irrigation"

        return "Healthy"


field = Field(
    "North Field",
    "Maize",
    29.5,
    42,
    70
)


print(field.name)
print(field.check_status())

Now your project is using object-oriented programming as well.

Saving the Project Data

You can save field information using JSON.

import json


field = {
    "name": "North Field",
    "crop": "Maize",
    "temperature": 29.5,
    "soil_moisture": 42,
    "humidity": 70
}


with open(
    "field.json",
    "w",
    encoding="utf-8"
) as file:

    json.dump(field, file, indent=4)

Later, your program can load the data again.

with open(
    "field.json",
    "r",
    encoding="utf-8"
) as file:

    field = json.load(file)

print(field["crop"])

Taking the Project to the Next Level

Once the software simulation works, you can gradually make the project more realistic.

For example:

  1. Use real sensor data.
  2. Connect an Arduino or Raspberry Pi.
  3. Store readings over time.
  4. Display the data in charts.
  5. Add a camera.
  6. Process images with OpenCV.
  7. Train an object-detection model.
  8. Detect crops or weeds.
  9. Connect the system to an agricultural robot.

Notice the progression:

Python basics
      ↓
Python application
      ↓
File/data storage
      ↓
Sensors
      ↓
Computer vision
      ↓
AI
      ↓
Robotics

This is how a simple programming project can eventually become the foundation for a much larger engineering system.

Good Project Habits

As your programs become larger, start developing professional habits.

1. Use Functions

Don't put your entire program inside one enormous block of code.

2. Use Meaningful Names

student_scores = [80, 90, 75]

is easier to understand than:

x = [80, 90, 75]

3. Handle Errors

Assume users will enter unexpected information.

4. Save Data Carefully

If your application stores important information, make sure data is not accidentally overwritten or lost.

5. Test Small Pieces

Test individual functions before combining everything.

6. Use Version Control

Git can help you track changes and return to earlier versions of your project.

7. Read Error Messages

An error message is not simply Python telling you that you failed. It is information about what Python encountered.

Final Challenge

Build your own Python application without following a complete tutorial.

Choose a problem you care about and design a solution.

For example:

  • Expense tracker
  • Inventory manager
  • Study planner
  • Weather information tool
  • Farm record manager
  • Plant observation tracker
  • Sensor monitoring simulator
  • Simple robotics control simulator
Your goal is not to write the perfect program. Your goal is to take a problem, break it down and build a working solution.

You Have Completed the Python Course

You have now moved through the major foundations of Python.

More importantly, you have reached the point where you can begin building your own programs instead of only following examples.

You Can Now Work With:

  • Variables and data types
  • Operators
  • Conditions
  • Loops
  • Strings
  • Lists
  • Tuples
  • Sets
  • Dictionaries
  • Functions
  • Modules
  • Files
  • Exceptions
  • Classes and objects
  • Dates and times
  • Regular expressions
  • Iterators and generators
  • Decorators
  • Packages and virtual environments

But completing a Python course does not mean you are finished learning Python.

It means you now have a foundation from which you can explore more specialized areas.

What Should You Learn Next?

Your next step should depend on what you want to build.

For Web Development

Explore frameworks such as Flask or Django.

For Data Science

Explore NumPy, pandas, Matplotlib and related tools.

For Artificial Intelligence

Learn NumPy, data processing, machine learning and deep learning.

For Computer Vision

Learn OpenCV and image processing before moving deeper into object detection.

For Robotics

Continue with Python while learning electronics, sensors, control systems, embedded programming and robotics frameworks.

For Agricultural Technology

Combine programming with agriculture, sensors, computer vision, data analysis and eventually robotics.

Don't try to learn everything at once. Choose a direction, build projects and learn the tools required to solve the problems in front of you.

Python Projects Quiz

1. What is the best way to improve your programming skills?



2. Which data structure is useful for storing key-value information?



3. Which module can generate random numbers?



4. Why are functions useful in larger projects?



5. Which format can be used to store structured data?



6. What should you do when you encounter an error?





Python Projects Summary

Projects are where your Python knowledge starts becoming practical.

In this module you worked through projects involving:

  • Calculators
  • Games
  • Currency conversion
  • Student score analysis
  • To-do lists
  • Quiz applications
  • Contact books
  • File analysis
  • Agricultural field monitoring

You also learned how a simple Python application can gradually evolve into a larger system involving files, sensors, computer vision, AI and robotics.

Final lesson: Don't measure your progress by how much Python syntax you can memorize. Measure it by how effectively you can use Python to solve problems.

0 Comments