Skip to content

Lesson 10

Python Conditionals

How a script makes decisions — if, elif, else, and the Boolean logic that ties them together.

You now have the data side of Python down: integers and floats, strings, tuples, lists, and dictionaries. Part 3 is about making the script do something with that data. The first move is the conditional: a statement that runs one piece of code if some test is true, and (optionally) a different piece of code if it isn’t.

A useful frame: programming is problem-solving in a logical, structured way. Conditionals are the most direct expression of that. When you write one, it helps to read it aloud in plain English first, then translate. “If the year is before 1700, mark it ‘early modern.’ Otherwise, mark it ‘modern.’” That sentence is almost the code already.

Booleans — the Yes/No type

Before we look at conditionals, you need to know about the type they live and die on. A Boolean is a value that is either True or False. Capital T, capital F — Python is strict about this. The two Booleans are returned by every comparison operator (==, <, >, !=, etc.) and accepted by every conditional.

print(1815 < 1850)        # True
print(1815 == 1850)       # False
print("war" in "warfare")  # True

if, elif, else

The basic shape is an if keyword, a test, a colon, and an indented block:

year = 1815
if year < 1700:
    print("early modern")

Indentation is not decoration. In Python, indentation defines the block. Four spaces is the convention. Anything indented under the if: belongs to the if; the first non-indented line below it is back in the main flow.

To handle multiple cases, chain elif (“else if”):

year = 1815
if year < 1500:
    print("medieval")
elif year < 1700:
    print("early modern")
elif year < 1800:
    print("18th-century")
else:
    print("modern")

The clauses are tested in order. As soon as one is true, its block runs and the rest are skipped. else (no test) catches anything none of the earlier clauses caught. Only one branch executes.

Truthiness — what counts as True

Conditionals don’t strictly need a Boolean. Python evaluates the test using truthiness: any value can be coerced to True or False.

The values that count as False:

  • False itself
  • The number 0 (and 0.0)
  • An empty string ""
  • An empty list [], tuple (), set set(), or dictionary {}
  • None

Everything else is True. This leads to two of the most common Python idioms:

items = []
if items:
    print("the list has stuff in it")
else:
    print("the list is empty")

name = ""
if not name:
    print("name is missing")

You almost never write if len(items) > 0: or if name != "": in idiomatic Python — the empty list and empty string are themselves falsy.

Combining conditions — and, or, not

The three Boolean operators are spelled as English words, not symbols:

year = 1815
country = "France"

if year >= 1789 and year <= 1799:
    print("French Revolution era")

if country == "France" or country == "USA":
    print("had a revolution in this period")

if not (year < 1700):
    print("after 1700")

Python lets you chain comparisons the way mathematics does, which often reads more cleanly than and:

year = 1815

if 1789 <= year <= 1799:
    print("French Revolution era")

A worked example — classifying letters

Here’s a small block that categorizes a letter record by both date and length. It exercises everything in this lesson:

letter = {"sender": "Voltaire", "year": 1759, "words": 320}

if letter["year"] < 1700:
    period = "early modern"
elif letter["year"] < 1800:
    period = "18th-century"
else:
    period = "modern"

if letter["words"] < 200:
    length = "short"
elif letter["words"] < 1000:
    length = "medium"
else:
    length = "long"

print(f"{letter['sender']}: a {length}, {period} letter")
# Voltaire: a medium, 18th-century letter

Two if/elif/else blocks doing two independent classifications. Each one is its own decision; they don’t interfere.

When to reach for a dict instead of a long elif chain

Here’s a refactor worth knowing once. If your conditional is doing nothing but mapping one value to another:

status = "draft"
if status == "draft":
    label = "Working copy"
elif status == "review":
    label = "Under review"
elif status == "published":
    label = "Live"
else:
    label = "Unknown"
print(label)

…it’s often clearer as a dictionary lookup with a default:

status = "draft"
labels = {
    "draft": "Working copy",
    "review": "Under review",
    "published": "Live",
}
label = labels.get(status, "Unknown")
print(label)

You don’t always want to do this — if each branch runs different code, not just produces a value, the if/elif is the right shape. But once you spot the pattern, the dictionary version is shorter and easier to extend.

Get the basics solid in the console, then continue to Lesson 11: Python Loops.

Running the code

Save any snippet from this lesson to a file — say try.py — and run it from your project folder:

uv run try.py

uv run uses the project’s Python and dependencies automatically; no virtualenv to activate. If you haven’t set the project up yet, Lesson 01 walks through it.