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

  1. The if statement: making the first decision
  2. else: the alternative path
  3. elif: chaining several conditions
  4. Nested conditionals
  5. Compound conditions with and, or and not
  6. Truthiness: values that behave like True or False
  7. 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:

if condition:
    # block that runs ONLY if the condition is true
    statement_1
    statement_2

Notice three fundamental details:

  • The condition is any expression Python can evaluate as True or False (the booleans you saw in module 1).
  • The colon (:) at the end of the if line 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:

'The Odyssey' is available (4 units).
Query finished.

What exactly happened?

  1. Python evaluates stock > 0. Since stock is 4, the expression gives True.
  2. Because it's true, the indented block runs (the print with the availability message).
  3. The last line, print("Query finished."), is not indented, so it's not part of the if: 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:

'Hamlet' is out of stock. Ana needs to restock it.

Rules for else:

  • It takes no condition of its own: it runs when the if condition is false.
  • It goes at the same indentation level as its if, also with a colon.
  • It's optional: an if can 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:

'Don Quixote': enough stock (8 units).

How Python evaluates it, in order and top to bottom:

  1. stock == 08 == 0 is False. That block is skipped.
  2. stock <= 38 <= 3 is False. Skipped too.
  3. No condition held, so the else runs.

Key points about if/elif/else chains:

  • You can add as many elif branches 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 else is 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 0

With 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:

Selling 'The Odyssey'...
Member discount applied: 11.88 EUR

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:

stock = 2
price = 15.90

if stock <= 3 or price > 15:
    print("Book flagged for Ana's review.")

Two important details about logical operators in conditions:

  • Short-circuit evaluation: in a and b, if a is False, Python doesn't even evaluate b (the result can no longer be True). In a or b, if a is True, it doesn't evaluate b. This is exploited to write safe conditions, such as if stock > 0 and 10 / stock < 3: — the division never runs if stock is 0.
  • Chained comparisons: Python lets you write if 1 <= stock <= 3: instead of if 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():

>>> bool(0)
False
>>> bool(4)
True
>>> bool("")
False
>>> bool("Papyrus")
True
>>> bool(None)
False

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:

value_if_true if condition else value_if_false

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:

stock = 4
label = "available" if stock else "out of stock"
print(f"The Odyssey — {label}")

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 if is 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, elif or else produces a SyntaxError. 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 elif branches 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.3 gives False. For money in conditionals, compare with a margin: abs(total - expected) < 0.001.
  • if is_member == True: works, but it's redundant: is_member is already a boolean. Write if 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_member and then if 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:

  1. If there's no stock, print "Sale not possible: no stock" and nothing else.
  2. If there is stock, calculate the price with BOOK_VAT = 0.04 included.
  3. Additionally apply MEMBER_DISCOUNT = 0.05 to the base price only if the customer is a member (the discount is applied before VAT).
  4. 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 float and 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

Module 2: Control Structures

Module 3: Functions and Modules

Module 4: Data Structures

Module 5: Object-Oriented Programming

Module 6: File Handling

Module 7: Error and Exception Handling

Module 8: Advanced Topics

Module 9: Testing and Debugging

Module 10: Web Development with Python

Module 11: Data Science with Python

Module 12: Final Project

© Copyright 2026. All rights reserved