Chapter 2: Control Flow
So far, your programs have run from top to bottom, one line after another. Control flow lets a program make decisions, repeat work, and group steps into reusable pieces.
In this chapter you will learn:
- How to run code only when a condition is true, with
if,elif, andelse - How to write short conditions as one-line expressions and compare a value against several options with
match - How to repeat code with
forandwhileloops, and howbreakandcontinuechange a loop’s flow - How to define functions with parameters, default values, and return values
- How variable scope works inside and outside functions
Conditional statements
A conditional statement runs a block of code only if a condition is met. This section covers:
ifstatementsif-elsestatementsif-elif-elsestatements- The
matchstatement - Conditional expressions (the ternary operator)
- Combining conditions with
and,or, andnot
if statements
The simplest conditional statement is the if statement. Its syntax is:
if condition:
# code to be executed if condition is True
Here, condition is an expression that evaluates to a boolean value (either True or False). If the condition is True, the code inside the if block runs. Otherwise, it is skipped.
The colon at the end of the if line and the indentation below it matter. Python uses indentation, by convention 4 spaces, to tell which lines belong to the block.
Here’s an example:
x = 5
if x > 0:
print("x is positive")
# Output:
# x is positive
if-else statements
To run one block of code when the condition is True and a different block when it is False, use the if-else statement:
if condition:
# code to be executed if condition is True
else:
# code to be executed if condition is False
Here’s an example:
x = -5
if x > 0:
print("x is positive")
else:
print("x is not positive")
# Output:
# x is not positive
if-elif-else statements
When you need to check several conditions and run different code depending on which one is met, use the if-elif-else statement (elif is short for “else if”):
if condition1:
# code to be executed if condition1 is True
elif condition2:
# code to be executed if condition1 is False and condition2 is True
elif condition3:
# code to be executed if condition1 and condition2 are False and condition3 is True
...
else:
# code to be executed if all conditions are False
Python checks the conditions from top to bottom and runs only the first block whose condition is True. You can have as many elif branches as you need, and the final else is optional.
Here’s an example:
x = 0
if x > 0:
print("x is positive")
elif x < 0:
print("x is negative")
else:
print("x is zero")
# Output:
# x is zero
The match statement
When you compare one value against several fixed options, a long if-elif-else chain can get repetitive. Python 3.10 and newer offer the match statement for this:
command = "exit"
match command:
case "start":
print("Starting the game")
case "quit" | "exit":
print("Goodbye")
case _:
print("Unknown command")
# Output:
# Goodbye
Python compares command against each case from top to bottom and runs the first one that matches. The | means “or”, so case "quit" | "exit": matches either word. The underscore _ matches anything, so case _: works like a final else. match can do much more, such as matching the shape of lists and dictionaries, but comparing a value against a few options is its most common use.
Conditional expressions (the ternary operator)
A conditional expression, also known as the ternary operator, picks one of two values based on a condition. It lets you write a simple if-else statement on a single line.
The syntax is:
value_if_true if condition else value_if_false
Here’s the same choice written both ways:
age = 20
# Traditional if-else statement
if age >= 18:
status = "Adult"
else:
status = "Minor"
print(status) # Output: Adult
# Equivalent conditional expression
status = "Adult" if age >= 18 else "Minor"
print(status) # Output: Adult
In this example, status is set to "Adult" if age is 18 or older, and "Minor" otherwise.
Another example
Suppose you want to describe whether a number is positive or not:
number = 5
result = "Positive" if number > 0 else "Not positive"
print(result) # Output: Positive
Nested conditional expressions
You can also nest conditional expressions, although this can make code harder to read:
number = 0
result = "Positive" if number > 0 else "Negative" if number < 0 else "Zero"
print(result) # Output: Zero
In this example, result is "Positive" if number is greater than 0, "Negative" if number is less than 0, and "Zero" if number is exactly 0.
Conditional expressions make code shorter, but use them only for simple choices. For anything more complex, a regular if-elif-else statement is easier to read.
Combining conditions with and, or, and not
You can combine conditions with the logical operators and, or, and not:
age = 25
has_ticket = True
if age >= 18 and has_ticket:
print("You can enter")
if not has_ticket:
print("Please buy a ticket")
# Output:
# You can enter
The truth table below shows the result of each operator for every combination of A and B:
| A | B | A and B | A or B | not A |
|---|---|---|---|---|
| False | False | False | False | True |
| False | True | False | True | True |
| True | False | False | True | False |
| True | True | True | True | False |
So and is True only when both sides are True, or is True when at least one side is True, and not flips a value.
Loops
Loops let you repeat a block of code multiple times. Python has two kinds: for loops and while loops.
for loops
A for loop goes through a sequence of elements, such as a list, tuple, or string, one element at a time. The syntax for a for loop is:
for element in sequence:
# code to be executed for each element
Here’s an example of a for loop that goes through a list of numbers and prints each number:
numbers = [1, 2, 3, 4, 5]
for number in numbers:
print(number)
# Output:
# 1
# 2
# 3
# 4
# 5
On each pass through the loop, the variable number holds the next item from the list.
Counting with range()
To repeat something a set number of times, use the range() function. range(5) produces the numbers 0 to 4: it starts at 0 and stops before 5.
for i in range(5):
print(i)
# Output:
# 0
# 1
# 2
# 3
# 4
You can also give range() a start value, a stop value, and a step value. This loop starts at 2, stops before 6, and counts up by 2:
for i in range(2, 6, 2):
print(i)
# Output:
# 2
# 4
Getting the position with enumerate()
If you need each item’s position as well as the item, use enumerate(). It gives you pairs of an index and an item, which you can unpack into two variables:
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
print(index, fruit)
# Output:
# 0 apple
# 1 banana
# 2 cherry
Counting starts at 0. To start at 1, write enumerate(fruits, start=1).
while loops
A while loop repeats a block of code as long as a condition is True. The syntax for a while loop is:
while condition:
# code to be executed while condition is True
Here’s an example of a while loop that prints the numbers 1 to 5:
i = 1
while i <= 5:
print(i)
i += 1
# Output:
# 1
# 2
# 3
# 4
# 5
Make sure something inside the loop eventually makes the condition False. Here, i += 1 increases i each time. Without it, i would stay 1 and the loop would run forever, which is called an infinite loop. If that happens, press Ctrl+C to stop the program.
break and continue
You can use the break and continue statements to control the flow of a loop. break exits the loop completely, while continue skips the rest of the current pass and moves on to the next one. Both work in for and while loops.
i = 1
while True:
if i > 5:
break
elif i % 2 == 0:
i += 1
continue
print(i)
i += 1
# Output:
# 1
# 3
# 5
while True would run forever on its own, so the break is what ends this loop once i goes past 5. For even numbers, continue jumps back to the top of the loop before print(i) runs, so only the odd numbers are printed.
Functions
A function is a named block of code that does one job. You define it once and then call it whenever you need that job done, instead of repeating the same code.
Defining and calling functions
To define a function, use the def keyword followed by the function name, a set of parentheses containing the function’s parameters, and a colon. The indented lines below form the function’s body:
def greet(name):
print("Hello, " + name)
greet("John") # Output: Hello, John
greet("Maria") # Output: Hello, Maria
Defining a function doesn’t run it. The body runs only when you call the function by writing its name followed by parentheses containing the values you want to pass in, as in greet("John").
Parameters and arguments
These two words are easy to mix up:
- A parameter is a variable name listed in the function definition. In
def greet(name):,nameis a parameter. - An argument is the actual value you pass when you call the function. In
greet("John"),"John"is the argument.
When the function runs, each parameter takes the value of its argument. So during the call greet("John"), name holds "John", and the function prints Hello, John.
Default values and keyword arguments
You can give a parameter a default value, which is used when the caller leaves that argument out. You can also pass arguments by name, called keyword arguments, so their order doesn’t matter:
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("John") # Output: Hello, John!
greet("John", "Good morning") # Output: Good morning, John!
greet(greeting="Hi", name="Sam") # Output: Hi, Sam!
The string is an f-string, which you met in Chapter 1: Python replaces {greeting} and {name} with their values. Parameters with default values must come after parameters without them in the definition.
Returning a value
Functions can also send a value back to the caller with the return keyword:
def add(x, y):
"""Return the sum of x and y."""
return x + y
result = add(3, 4)
print(result) # Output: 7
print(add(10, 5) * 2) # Output: 30
The add function has two parameters, x and y, and returns their sum. When you call it with the arguments 3 and 4, it returns the value 7, which is stored in the result variable. You can use a returned value anywhere you could use the value itself, such as in a calculation.
The string in triple quotes right below the def line is a docstring. It describes what the function does, and tools such as help(add) display it. Adding one to each function you write is a good habit.
A function without a return statement returns the special value None. Printing inside a function is not the same as returning:
def greet(name):
print("Hello, " + name)
value = greet("John") # Output: Hello, John
print(value) # Output: None
Scope and global variables
The scope of a variable is the part of the program where you can use it. There are two main kinds of scope:
- A global variable is created outside any function. You can read it anywhere in the file, including inside functions.
- A local variable is created inside a function, and parameters are local too. It exists only while the function runs.
For example:
x = 10 # global variable
def func1():
print(x) # reads the global x
def func2():
x = 20 # creates a new local variable named x
print(x)
func1() # Output: 10
func2() # Output: 20
print(x) # Output: 10
The func1 function has no variable named x of its own, so it reads the global x and prints 10.
The func2 function assigns to x, which creates a local variable that shadows the global variable with the same name. Inside func2, x means the local variable, so it prints 20. Outside func2, the global x is unchanged and still has the value 10.
Changing a global variable
Assigning to a variable inside a function always creates a local variable, unless you say otherwise. To change a global variable from inside a function, declare it with the global keyword first:
count = 0
def increment():
global count
count += 1
increment()
increment()
print(count) # Output: 2
Without the global line, count += 1 fails. Python sees the assignment, treats count as a local variable for the whole function, and then finds that this local variable has no value yet when it tries to add 1. The result is an UnboundLocalError. The try and except lines below catch the error and print its message instead of stopping the program. You’ll learn about them in Chapter 4.
count = 0
def increment():
count += 1 # count is local here, but has no value yet
try:
increment()
except UnboundLocalError as error:
print(error)
# Output:
# cannot access local variable 'count' where it is not associated with a value
Python 3.10 words this message differently, but the error is the same.
Using global works, but it is usually better to pass values in as arguments and return the result. A function that only uses its parameters and return value is easier to understand, test, and reuse:
def increment(count):
return count + 1
count = 0
count = increment(count)
count = increment(count)
print(count) # Output: 2
Key takeaways
if,elif, andelserun different code depending on conditions. Python runs only the first block whose condition isTrue.match(Python 3.10+) compares one value against several options, andcase _catches everything else.forloops go through each item in a sequence.range()produces a series of numbers, andenumerate()gives you each item’s index too.whileloops repeat as long as a condition isTrue, so make sure it eventually becomesFalse.breakleaves a loop early, andcontinueskips to the next pass.- Functions receive arguments through their parameters, can have default values, and send results back with
return. - Assigning to a variable inside a function makes it local. Prefer returning values over changing global variables with
global.
Practice
Exercise 1: Letter grades
Store a test score from 0 to 100 in a variable named score. Print the letter grade: A for 90 and above, B for 80 to 89, C for 70 to 79, D for 60 to 69, and F below 60. Try several scores to check every branch.
Solution
score = 82
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
else:
grade = "F"
print(f"A score of {score} gets a {grade}")
# Output:
# A score of 82 gets a B
Exercise 2: FizzBuzz
Print the numbers from 1 to 15. For multiples of 3, print Fizz instead of the number. For multiples of 5, print Buzz. For multiples of both 3 and 5, print FizzBuzz.
Solution
for number in range(1, 16):
if number % 3 == 0 and number % 5 == 0:
print("FizzBuzz")
elif number % 3 == 0:
print("Fizz")
elif number % 5 == 0:
print("Buzz")
else:
print(number)
# Output:
# 1
# 2
# Fizz
# 4
# Buzz
# Fizz
# 7
# 8
# Fizz
# Buzz
# 11
# Fizz
# 13
# 14
# FizzBuzz
The check for both 3 and 5 has to come first. If it came after the check for 3, Python would print Fizz for 15 and never reach it.
Exercise 3: Counting vowels
Write a function count_vowels(text) with a docstring. It should return the number of vowels (a, e, i, o, u) in text, counting uppercase and lowercase letters. Test it on "Hello, World" and "Python".
Solution
def count_vowels(text):
"""Return the number of vowels in text."""
count = 0
for letter in text.lower():
if letter in "aeiou":
count += 1
return count
print(count_vowels("Hello, World")) # Output: 3
print(count_vowels("Python")) # Output: 1
text.lower() converts the text to lowercase, so you only need to check lowercase vowels.