At the end of the previous module we left the Papyrus project with its environment ready and several scripts running, and we promised that your programs would start making decisions. This lesson delivers on that promise: you'll learn to use if, elif and else so that Python runs one piece of code or another depending on which conditions hold. It's the difference between a script that always does the same thing and a program that reacts: "is The Odyssey in stock?", "is Luis a member? Then apply his discount". Without conditionals there is no business logic to speak of, so this is probably the most important lesson of the course so far.
Contents
- The
ifstatement: making the first decision else: the alternative pathelif: chaining several conditions- Nested conditionals
- Compound conditions with
and,orandnot - Truthiness: values that behave like
TrueorFalse - The ternary operator: conditionals in one line
The if statement: making the first decision
The basic structure of a conditional in Python is the if statement. Its syntax is:
Notice three fundamental details:
- The condition is any expression Python can evaluate as
TrueorFalse(the booleans you saw in module 1). - The colon (
:) at the end of theifline is mandatory. Forgetting it is the most frequent syntax error when starting out. - The indented block (4 spaces, as you already know from PEP 8) defines which statements depend on the
if. In Python, indentation isn't cosmetic: it is the structure of the program.
Let's solve the first question Ana asks herself every time someone requests a book at Papyrus: is it in stock?
title = "The Odyssey"
stock = 4
if stock > 0:
print(f"'{title}' is available ({stock} units).")
print("Query finished.")Output:
What exactly happened?
- Python evaluates
stock > 0. Sincestockis4, the expression givesTrue. - Because it's true, the indented block runs (the
printwith the availability message). - The last line,
print("Query finished."), is not indented, so it's not part of theif: it always runs, whether there's stock or not.
If you change stock = 0 and run it again, you'll see only Query finished.: the if block is skipped entirely.
The flow of an if, as a diagram
flowchart TD
A[Start] --> B{stock > 0}
B -- True --> C[Show availability]
B -- False --> D[Continue]
C --> D
D --> E[Query finished]
The program reaches a fork, picks a path according to the condition, and then both paths join up again.
else: the alternative path
Very often "do this if it holds" isn't enough: you also want to do something else if it doesn't. That's what else is for:
title = "Hamlet"
stock = 0
if stock > 0:
print(f"'{title}' is available ({stock} units).")
else:
print(f"'{title}' is out of stock. Ana needs to restock it.")Output:
Rules for else:
- It takes no condition of its own: it runs when the
ifcondition is false. - It goes at the same indentation level as its
if, also with a colon. - It's optional: an
ifcan perfectly well stand alone, as in the first example.
With if/else you guarantee that exactly one of the two blocks always runs.
elif: chaining several conditions
What if there are more than two scenarios? Ana wants to classify each book's stock into three levels: out of stock, only a few units left, or enough stock. To chain conditions you use elif (a contraction of else if):
title = "Don Quixote"
stock = 8
if stock == 0:
print(f"'{title}': OUT OF STOCK. Order from the distributor.")
elif stock <= 3:
print(f"'{title}': only a few units left ({stock}).")
else:
print(f"'{title}': enough stock ({stock} units).")Output:
How Python evaluates it, in order and top to bottom:
stock == 0→8 == 0isFalse. That block is skipped.stock <= 3→8 <= 3isFalse. Skipped too.- No condition held, so the
elseruns.
Key points about if/elif/else chains:
- You can add as many
elifbranches as you need. - As soon as one condition gives
True, its block runs and the rest of the chain is ignored, even if other conditions would also have been true. Order matters. - The final
elseis optional, but it's usually a good idea to include it as a "default case".
That second point deserves an example. Watch what happens if we order the conditions badly:
stock = 0
# BADLY ordered: the broad condition comes first
if stock <= 3:
print("Only a few units left.") # This runs!
elif stock == 0:
print("OUT OF STOCK.") # Never reached with stock 0With stock = 0, the first condition (0 <= 3) is already true, so the "OUT OF STOCK" message will never be shown. Practical rule: put the most specific conditions first and leave the more general ones for the end.
Nested conditionals
An if block can, in turn, contain another if. That's what we call nesting, and it's used for decisions that depend on a previous decision. At Papyrus: first we check whether there's stock and, only if there is, we check whether the customer is a member so we can apply the discount.
title = "The Odyssey"
price = 12.50
stock = 4
is_member = True # Luis is a Papyrus member
MEMBER_DISCOUNT = 0.05
if stock > 0:
print(f"Selling '{title}'...")
if is_member:
final_price = price * (1 - MEMBER_DISCOUNT)
print(f"Member discount applied: {final_price:.2f} EUR")
else:
final_price = price
print(f"Price without discount: {final_price:.2f} EUR")
else:
print(f"Cannot sell '{title}': no stock.")Output:
Look at the indentation: the if is_member: is indented 4 spaces (inside the first if), and its inner blocks are at 8 spaces. Each level of nesting adds 4 spaces.
Style tip: two levels of nesting read well; three start to hurt; four or more usually mean the code needs reorganizing (often with compound conditions, which we'll see next, or with functions, which you'll see in module 3).
Compound conditions with and, or and not
You already know the logical operators from module 1. This is precisely their big moment: combining several checks into a single condition.
| Operator | Result | Example | Evaluates to |
|---|---|---|---|
and |
True only if both parts are True |
stock > 0 and is_member |
True if there's stock and they're a member |
or |
True if at least one part is True |
stock == 0 or price > 50 |
True if it's out of stock or it's expensive |
not |
Inverts the value | not is_member |
True if they're not a member |
With and we can rewrite the previous example without nesting, when we only care about one specific case:
title = "The Odyssey"
price = 12.50
stock = 4
is_member = True
MEMBER_DISCOUNT = 0.05
if stock > 0 and is_member:
final_price = price * (1 - MEMBER_DISCOUNT)
print(f"Member sale: '{title}' for {final_price:.2f} EUR")
elif stock > 0:
print(f"Regular sale: '{title}' for {price:.2f} EUR")
else:
print(f"'{title}' is out of stock.")And with or, Ana can spot books that need her attention for either of two reasons:
Two important details about logical operators in conditions:
- Short-circuit evaluation: in
a and b, ifaisFalse, Python doesn't even evaluateb(the result can no longer beTrue). Ina or b, ifaisTrue, it doesn't evaluateb. This is exploited to write safe conditions, such asif stock > 0 and 10 / stock < 3:— the division never runs ifstockis0. - Chained comparisons: Python lets you write
if 1 <= stock <= 3:instead ofif stock >= 1 and stock <= 3:. Both are equivalent, but the first is more readable and very idiomatic.
When a compound condition mixes and and or, use parentheses to make the intent clear (remember that and has priority over or):
# Does the promotion apply? Members, or expensive purchases of classics
if is_member or (price > 15 and is_classic):
...Truthiness: values that behave like True or False
So far our conditions were explicit comparisons (stock > 0). But Python lets you put any value as a condition, and it will convert it to a boolean automatically. This implicit conversion is called truthiness.
The rule is easy to remember: empty and null things are false; everything else is true. These are the most common falsy values (values that evaluate to False):
| Value | Type | Why is it falsy? |
|---|---|---|
False |
bool | It's the literal false |
0, 0.0 |
int, float | Numeric zero |
"" |
str | Empty string |
None |
NoneType | Absence of a value |
[], (), {} |
collections | Empty collections (you'll see them in module 4) |
Everything else is truthy: 1, -3, 0.01, "hello", " " (a string with a space in it isn't empty!), and so on.
You can check it in the REPL with bool():
What is it good for in practice? Writing more natural conditions. These two versions are equivalent, and the second is the idiomatic one in Python:
stock = 4
# Explicit version
if stock > 0:
print("In stock.")
# Idiomatic version using truthiness
if stock:
print("In stock.")And it's very useful with input(), which as you'll remember always returns str. If the user presses Enter without typing anything, it returns the empty string "", which is falsy:
name = input("Customer name (press Enter to skip): ")
if not name:
name = "Anonymous customer"
print(f"Welcome to Papyrus, {name}.")Here not name is True when name is empty, so we assign a default value. This pattern (checking whether an input is empty) comes up constantly in real programs.
Nuance: use truthiness when the natural question is "is there anything here?" (is there text?, are there units?). If the question is about a specific value ("is the stock exactly 0?", "is the answer 'y'?"), the explicit comparison (==) communicates the intent better.
The ternary operator: conditionals in one line
When all an if/else does is choose between two values, Python offers a compact form called a conditional expression or ternary operator:
Compare the classic version with the ternary one to work out Luis's price:
price = 9.95 # Hamlet
is_member = True
MEMBER_DISCOUNT = 0.05
# Classic version: 4 lines
if is_member:
final_price = price * (1 - MEMBER_DISCOUNT)
else:
final_price = price
# Ternary version: 1 line, same result
final_price = price * (1 - MEMBER_DISCOUNT) if is_member else price
print(f"Final price: {final_price:.2f} EUR")It also shines inside f-strings, for example on the Papyrus receipt:
Rules for using the ternary:
- It's an expression: it produces a value, so you can assign it, print it or pass it to a function. A regular
ifis a statement and produces no value. - It reads "A if condition, otherwise B", which is almost natural language.
- Use it only for simple choices between two values. If you need to run several statements, or nest ternaries inside ternaries, go back to the classic
if/else: readability rules.
Common Mistakes and Tips
- Forgetting the colon at the end of
if,eliforelseproduces aSyntaxError. It's every beginner's number-one slip; check it before anything else. - Confusing
=with==:if stock = 0:is a syntax error.=assigns,==compares. Python protects you here by refusing to run it. - Inconsistent indentation: mixing 4 spaces with tabs or with 3 spaces causes an
IndentationError. Configure your editor (VS Code does it by default) to insert 4 spaces when you press Tab. - Ordering the
elifbranches badly: as you saw, if a broad condition comes before a specific one, the specific one will never run. Order from most specific to most general. - Comparing floats with
==: because of the decimal imprecision we saw in module 1,0.1 + 0.2 == 0.3givesFalse. For money in conditionals, compare with a margin:abs(total - expected) < 0.001. if is_member == True:works, but it's redundant:is_memberis already a boolean. Writeif is_member:and, for the opposite case,if not is_member:.- Tip: when a compound condition becomes hard to read, store it in a variable with a descriptive name:
can_buy = stock > 0 and is_memberand thenif can_buy:. The code documents itself.
Exercises
Exercise 1: stock classifier
Write a script stock_level.py that, given the variables title and stock, prints:
"OUT OF STOCK — order urgently"if the stock is 0."Low stock — restock soon"if it's between 1 and 3 (inclusive)."Stock OK"if it's between 4 and 10."Excess stock — don't order more"if it's greater than 10.
Try it with the three books in the catalog: The Odyssey (4), Hamlet (6) and Don Quixote (8).
Exercise 2: final price of a sale
Extend member_sale.py: given the variables price, is_member and stock, the script must:
- If there's no stock, print
"Sale not possible: no stock"and nothing else. - If there is stock, calculate the price with
BOOK_VAT = 0.04included. - Additionally apply
MEMBER_DISCOUNT = 0.05to the base price only if the customer is a member (the discount is applied before VAT). - Print the final price with two decimals.
Check that for Don Quixote (15.90 EUR) bought by Luis (a member) the result is 15.71 EUR.
Exercise 3: book entry validator
In book_card.py we promised to validate the input. Take the first step: use input() to ask for a book's title and its price. Using truthiness and a ternary:
- If the title is empty, print
"Error: the title is required". - Otherwise, convert the price to
floatand print, in a single f-string with a ternary,"<title>: budget"if it costs less than 12 EUR or"<title>: premium"otherwise.
Solutions
Solution 1:
title = "The Odyssey"
stock = 4
if stock == 0:
message = "OUT OF STOCK — order urgently"
elif stock <= 3:
message = "Low stock — restock soon"
elif stock <= 10:
message = "Stock OK"
else:
message = "Excess stock — don't order more"
print(f"{title}: {message}")Notice that you don't need to write 1 <= stock <= 3 in the second case: if we reach that elif, we already know stock isn't 0, so stock <= 3 is enough. Each branch can rely on the previous ones having failed. All three catalog books fall into "Stock OK".
Solution 2:
BOOK_VAT = 0.04
MEMBER_DISCOUNT = 0.05
price = 15.90 # Don Quixote
is_member = True # Luis
stock = 8
if stock == 0:
print("Sale not possible: no stock")
else:
base = price * (1 - MEMBER_DISCOUNT) if is_member else price
final_price = base * (1 + BOOK_VAT)
print(f"Final price: {final_price:.2f} EUR")Step-by-step calculation: 15.90 × 0.95 = 15.105 (member discount) and 15.105 × 1.04 = 15.7092, which the f-string rounds to 15.71 EUR. The ternary resolves the choice of base price in one line.
Solution 3:
title = input("Book title: ")
if not title:
print("Error: the title is required")
else:
price = float(input("Price (EUR): "))
print(f"{title}: {'budget' if price < 12 else 'premium'}")not title is True when the user presses Enter without typing (empty string, falsy). The ternary goes inside the f-string, between braces, because it's an expression and produces a value. Note: if the user types a non-numeric price, float() will fail with a ValueError; we'll learn to handle that gracefully in module 7.
Conclusion
Your programs can now decide. You've learned the if statement together with its companions elif and else, how to nest decisions when one depends on another, how to combine checks with and/or/not (including short-circuiting), how to take advantage of the truthiness of empty values, and how to condense simple choices with the ternary operator. Papyrus can now answer its two essential business questions: whether there's stock and whether the member discount applies.
But we're still deciding book by book, with loose variables. Ana has a whole catalog, and checking the stock of every title by copy-pasting ifs doesn't scale. What we need is to repeat an operation over many items, and that's exactly what the next lesson is about: the for and while loops, with which we'll walk through the entire Papyrus catalog and add up the day's takings in just a few lines.
Python Programming Course
Module 1: Introduction to Python
- Introduction to Python
- Setting Up the Development Environment
- Python Syntax and Basic Data Types
- Variables and Constants
- Basic Input and Output
- Virtual Environments and Package Management
Module 2: Control Structures
Module 3: Functions and Modules
- Defining Functions
- Function Arguments
- Lambda Functions
- Modules and Packages
- Standard Library Overview
Module 4: Data Structures
Module 5: Object-Oriented Programming
Module 6: File Handling
Module 7: Error and Exception Handling
- Introduction to Exceptions
- Handling Exceptions
- Raising Exceptions
- Custom Exceptions
- Best Practices and Error Logging
Module 8: Advanced Topics
- Type Hints
- Decorators
- Generators
- Context Managers
- Concurrency: Threads and Processes
- Asyncio for Asynchronous Programming
Module 9: Testing and Debugging
- Introduction to Testing
- Unit Testing with unittest
- Testing with pytest
- Test-Driven Development
- Debugging Techniques
- Using pdb for Debugging
Module 10: Web Development with Python
- Introduction to Web Development
- Flask Framework Fundamentals
- Building REST APIs with Flask
- Introduction to Django
- Building Web Applications with Django
Module 11: Data Science with Python
- Introduction to Data Science
- NumPy for Numerical Computing
- Pandas for Data Manipulation
- Matplotlib for Data Visualization
- Introduction to Machine Learning with scikit-learn
