South Plantation High School logo
South Plantation High School · Procedural Programming with Python

Multiple & Nested Conditionals

Complex Boolean expressions · Multiple if statements · Nested if statements · Lesson review

Review the lesson slides, then complete the assignment at the bottom of this page. Your assignment work is saved in this browser on this computer. When you check a task, your name, period, and which tasks are complete are shared with your teacher (your code is not). When you finish, print it or save it as a PDF to turn in.
1 / 18
Lesson Review

Multiple & Nested Conditionals

andornotif inside if

Asking more than one question at a time.

By the end of this lesson you will be able to:

  • Build complex Boolean expressions with and, or, and not.
  • Work out a complex expression step by step, the same way Python does.
  • Use parentheses to control the order Python checks things.
  • Decide when to use several separate if statements.
  • Write nested if statements (an if inside another if).
  • Spot the most common mistakes and error messages.
How to use this page: Use Next and Back (or the arrow keys). In a TRY IT editor, change the values and click ▶ Run. Some editors also show how Python works out an expression, one step at a time.

Words you will hear today

WordWhat it means
Boolean expressionAnything that works out to True or False, like age >= 13
complex conditionTwo or more conditions joined with and / or, possibly with not
truth tableA chart showing the result for every possible True/False combination
precedenceThe order Python uses to work out an expression
nested ifAn if written inside the body of another if or else
logic errorA mistake that runs without an error message but gives the wrong answer
  • Real decisions often depend on more than one thing.
  • Today: combining conditions, and putting decisions inside decisions.
  • Complex condition, truth table, precedence, nested if, logic error.

Last time, each decision we wrote asked one question. Real programs usually need more. Can you ride the roller coaster? That depends on your height and maybe your age and maybe whether an adult is with you. Today we learn how to ask several questions at once, and how to put one decision inside another.

Warm Up

Quick review: Boolean values

A Boolean value is either True or False. Every condition in an if statement becomes one of these two values.

OperatorMeaningExampleResult
==equal to4 == 4True
!=not equal to4 != 4False
> <greater / less than9 < 2False
>= <=or equal to7 >= 7True

Boolean variables

A variable can hold True or False, just like it can hold a number or a string. You can also store the result of a comparison.

is_raining = True               # a Boolean value
score = 82
passed = score >= 70            # passed holds True

if passed:
    print("Nice work!")
Nice work!
Tip: if passed: already checks for True. You do not need if passed == True:. Both work, but the short version is how Python programmers write it.

Naming Boolean variables

Good Boolean names read like a yes/no question, so the if line reads like a sentence:

  • is_raining, has_key, can_vote, passed
  • if has_key: reads as "if (the player) has (the) key."

Avoid names that do not sound like a question, like key or rain. A reader cannot tell whether they hold True/False, a number, or a string.

Why store a condition? It gives a long condition a short, meaningful name, and you can reuse it: can_ride = height >= 48 and age >= 10, then if can_ride: anywhere later.

  • True and False are capitalized.
  • A comparison can be stored in a variable.
  • if passed: is the same as if passed == True:.
  • Name Booleans like questions: is_, has_, can_.

Before we combine conditions, remember the building blocks. Every comparison turns into True or False. And we can store that answer in a variable with a good name, like passed. That makes long conditions much easier to read, which you will see in a few slides.

Complex Boolean Expressions

and & or

and and or join two conditions into one bigger condition.

  • A and B is True only when both A and B are True.
  • A or B is True when at least one of them is True.

A truth table lists every possible combination. Fill in the two tables, then click Check.

Real life: "You can go to the game if you finish your homework and clean your room" needs both. "You can pay with cash or a card" needs just one.

How to fill in a truth table quickly

  • For and: find the one row where both are True. That row is True; every other row is False.
  • For or: find the one row where both are False. That row is False; every other row is True.

Python takes shortcuts

Python stops checking as soon as it knows the answer. This is called short-circuit evaluation:

  • False and ... is False no matter what comes next, so Python skips the right side.
  • True or ... is True no matter what, so Python skips the right side.

This can prevent errors. In x != 0 and 10 / x > 2, if x is 0, the left side is False, so Python never tries to divide by zero.

  • and: both must be True.
  • or: one True is enough. It is only False when both sides are False.
  • Short-circuit: and stops at the first False, or stops at the first True.

Truth tables show every possible case. There are only four rows because each side can only be True or False. Fill these in on your own before you click Check. Here is a shortcut to remember: and is only True in one row, and or is only False in one row.

Complex Boolean Expressions

not flips the answer

Anot A
TrueFalse
FalseTrue

not is useful when you want to check that something is not the case:

is_raining = False
if not is_raining:
    print("Let's go to the park!")
Let's go to the park!
Same meaning, two ways: not score >= 70 and score < 70 always give the same answer. Use whichever is easier to read. Usually the version without not is clearer.

Below, needs_help is True when the score is not passing. Change the score and watch Python work it out one step at a time.

Flipping a whole and or or

Putting not in front of a combined condition flips both parts and swaps the joining word. This is called De Morgan's law:

This......means the same as
not (sunny and warm)not sunny or not warm
not (cash or card)not cash and not card

In words: "It is not sunny and warm" means at least one of them is missing. "You have neither cash nor card" means both are missing.

Watch the precedence: not grabs only what is right next to it. not a and b means (not a) and b. To flip a whole expression, wrap it in parentheses.

  • not True is False. not False is True.
  • Python does the comparison first, then the not.
  • not (A and B) = not A or not B.
  • not applies only to what is right after it.

Not is the simplest one: it flips True to False and False to True. Watch the steps in the editor. Python first works out the comparison inside the parentheses, and only then applies the not. That order is exactly what the next slide is about.

Complex Boolean Expressions

Order of operations

Just like math has an order of operations, so do Boolean expressions. Python works from the top of this list to the bottom:

OrderWhatExample
1stParentheses( )
2ndMath+ - * / %
3rdComparisons== != < > <= >=
4thnot
5thand
6thor
Memory trick: and is like multiplication and or is like addition. Just like 2 + 3 * 4 does the * first, A or B and C does the and first.

Same values, different parentheses. Run both and compare the steps.

Solving one by hand

With age = 15 and member = False, work out age > 12 and age < 18 or member:

age > 12 and age < 18 or member
# = True and True or False      comparisons first
# = True or False               and before or
# = True                        or last

Each line replaces one part with its value. Work slowly and you will get the same answer as Python every time.

  • Comparisons, then not, then and, then or.
  • Parentheses always go first.
  • When in doubt, add parentheses. They make your meaning clear to Python and to people.
  • Replace one piece at a time: comparisons, then not, then and, then or.

Run the first editor. Even though or comes first when you read left to right, Python does the and first, so the answer is True. In the second editor, the parentheses force the or to go first, and now the answer is False. Same values, different answer. Parentheses matter.

Complex Boolean Expressions

Parentheses can fix a bug

Rule for a roller coaster: riders must be at least 48 inches tall, and they must be 10 or older or riding with an adult. The rider below is 40 inches tall. Run both versions.

Without parentheses
With parentheses
The bug: without parentheses, Python reads it as (height >= 48 and age >= 10) or with_adult. Any rider with an adult can ride, even if they are too short! The parentheses on the right make sure the height rule always applies.

Reading the rule out loud helps

When a sentence has an "and" and an "or," listen for which words belong together. "At least 48 inches tall, and (10 or older or with an adult)." The part in the pause is the part that needs parentheses.

Test the case that should fail

Both versions let a tall 12-year-old ride, so testing only with "good" riders would never find this bug. The bug only shows with a rider who breaks a rule, like the short rider with an adult. Always ask: "Who should be turned away? Does my program turn them away?"

  • Without parentheses, and groups first.
  • Use parentheses to group the or part when it should be checked together.
  • Test with values that should fail, not just values that should pass.
  • Test the riders who should be turned away.

This is a real kind of bug. The program does not crash. It just lets the wrong person on the ride. Look at the steps on the left: the and gets done first, then the or with with_adult makes the whole thing True. On the right, the parentheses keep the height check in charge. Now try changing the height to 50 and run both again.

Complex Boolean Expressions

Checking a range of values

Inside a range: use and

"Is x between 1 and 10?" means x must be at least 1 and at most 10.

if x >= 1 and x <= 10:
    print("In range")

Python also lets you write this as a chained comparison, which reads like math:

if 1 <= x <= 10:
    print("In range")

Outside a range: use or

"Is x outside 1 to 10?" means x is too small or too big. It cannot be both at once.

if x < 1 or x > 10:
    print("Out of range")
Careful: x >= 1 and <= 10 is a SyntaxError. Each side of and must be a full comparison, so you must repeat x.

Try 0, 1, 5, 10, and 11. Notice that the two editors always give opposite answers.

Why and for inside, or for outside?

Picture a number line from 1 to 10. To be inside, a number has to pass two tests at once: not too small and not too big. To be outside, failing either test is enough: too small or too big.

A range check that can never be True: x < 1 and x > 10. No number is both smaller than 1 and bigger than 10, so the body never runs. If an "outside" check never fires, check whether you used and where you meant or.

Inside vs. outside are opposites: not (1 <= x <= 10) gives the same answer as x < 1 or x > 10.

  • Inside a range: and. Outside a range: or.
  • Repeat the variable on both sides of and / or.
  • Check the edge values (like 1 and 10) to make sure you used <= or < correctly.
  • Inside: both tests pass (and). Outside: either test fails (or).

Checking a range is one of the most common uses of and. Here is a question for you: why would x less than 1 and x greater than 10 never be True? Right, no number can be smaller than 1 and bigger than 10 at the same time. That is why outside the range uses or.

Complex Boolean Expressions

The or trap

This program should only say "Nice!" for red or blue. Type green and run both versions.

Looks right, but is wrong
Fixed
Why does green pass? Python reads the left version as (color == "red") or ("blue"). The right side is just the string "blue" by itself, not a comparison. Python treats any non-empty string like True, so the whole condition is always True. No error message, just the wrong answer.
The fix: write a complete comparison on each side: color == "red" or color == "blue".

Another way to fix it: in

When you are checking one variable against several possible values, in with a list is shorter and avoids the trap completely:

if color in ["red", "blue"]:
    print("Nice!")

This reads as "if color is one of red or blue," which is exactly what we meant.

Why is "blue" treated as True? Python lets any value be used as a condition. Empty things ("", 0) count as False, and everything else counts as True. So a non-empty string by itself is always "true enough."

  • x == "a" or "b" is always True. It is a logic error, not a syntax error.
  • Always write the full comparison on both sides of or and and.
  • color in ["red", "blue"] avoids the or trap.

This is probably the most common mistake beginners make with or. In English, "if the color is red or blue" sounds perfect. But Python needs the full comparison on both sides. Run the left side with green, purple, anything. It always says Nice. Then run the fixed version.

Multiple Conditionals

Multiple separate if statements

When you have several independent questions, use several separate if statements. Python checks every one, so zero, one, or many of them can run.

Packing for the day: you might need a jacket and an umbrella. One answer does not cancel the others, so an elif chain would be the wrong tool.

Use...When...
separate ifseach question is independent, and more than one can be True
if-elif-elseonly one choice should happen

Notice line 9 uses a complex condition: a hat only helps when it is windy and not raining.

ranskipped

How to decide in one question

Ask: "If this one is True, should Python still check the next one?"

  • Yes: separate ifs. Cold and rainy means jacket and umbrella.
  • No: an elif chain. If the grade is already an A, Python should not also give a B.

A useful test: with separate ifs, try values that make none true, one true, and all true. Every combination should give a sensible result.

  • Separate ifs are all checked, top to bottom.
  • An elif chain stops at the first True.
  • If one is True, should the next still be checked? Yes: separate ifs.

Try 50 degrees with rain. You get a jacket and an umbrella. Two bodies ran, because these are separate ifs. Now turn on windy and turn off rain. What changes? These are independent checks, so each one gets its own if.

Multiple Conditionals

elif chains with complex conditions

Any condition in an if or elif can be a complex expression. Here a movie theater picks one price for each ticket:

  • Kids under 13 or adults 65 and up pay $8.
  • Otherwise, students pay $10.
  • Everyone else pays $12.

Only one price can apply, so this is an if-elif-else chain. Remember: top to bottom, first True wins.

Try it: age 10 with is_student checked. The student line is never checked, because the first condition was already True.
ranskipped

Trace age 70, not a student

  1. age < 13 or age >= 65: False or True is True, so the price is $8.
  2. The rest of the chain is skipped.

Trace age 16, a student

  1. age < 13 or age >= 65: False or False is False. Move on.
  2. The student check is True, so the price is $10.

The order sets the rules. Because the age discount comes first, a 10-year-old student pays $8, not $10. If the theater wanted students to always pay $10, the student check would have to come first.

  • Complex conditions work in if, elif, and nested ifs.
  • In a chain, the first True condition wins.
  • The order of an elif chain decides which rule wins.

This combines two things: an elif chain, which picks one option, and a complex condition using or. Run it with age 70, then age 16 as a student, then age 30 who is not a student. Each person gets exactly one price.

Nested Conditionals

Nested if: a decision inside a decision

A nested if is an if statement inside the body of another if (or else). The inner question is only asked when the outer answer leads there.

Logging in to an app works this way:

  1. First: does this username exist?
  2. Only if it does, check the password.
if username == "coder1":
    if password == "py123":
        print("Welcome back!")
    else:
        print("Wrong password.")
else:
    print("User not found.")
  • Each level of nesting adds 4 more spaces.
  • Each else belongs to the if it lines up with.
Nested decision flowchart First question: is the username correct? If False, print User not found. If True, a second question: is the password correct? If True, print Welcome back. If False, print Wrong password. Username correct? False User not found True inner (nested) if Password correct? True False Welcome back Wrong password

The password question lives inside the True path of the username question.

Why nest instead of using and?

You could write if username == "coder1" and password == "py123":, but then a failed login could only say "Login failed." Nesting lets the program know which check failed, so it can give a specific message. Each else answers a different question.

Reading nested code

  • Find the outer if at the left. Its body is everything indented under it.
  • Inside that body is a complete, smaller if/else. Read it as its own little program.
  • The outer else lines up with the outer if, so it answers the username question.

Real apps usually show the same "username or password is wrong" message for both, so attackers cannot learn which usernames exist. We use separate messages here so you can see each path.

  • Nested if = an if inside another if or else body.
  • The inner if only runs when the outer path leads to it.
  • Nesting lets each failed check give its own message.

Look at the dashed box in the flowchart. That whole box is the inner if. If the username is wrong, Python never even looks at the password. In the code, the inner if is indented 4 spaces, and its body is indented 8 spaces.

Nested Conditionals

Try it: nested login

The correct username is coder1 and the password is py123. Try all three paths: correct login, correct username with a wrong password, and a wrong username.

ranskipped
Notice: with a wrong username, lines 4 to 7 are skipped completely. The inner if is never checked. Also try Coder1 with a capital C. String comparisons are case sensitive.

Plan your tests before you click

UsernamePasswordExpected path
coder1py123outer True, inner True: Welcome back!
coder1wrongouter True, inner False: Wrong password.
someone(anything)outer False: User not found.

A nested if with an else at each level has one path per row. Testing each row once is called testing every path.

  • Three paths: welcome, wrong password, user not found.
  • The inner if is skipped entirely when the outer condition is False.
  • Make a table of expected paths, then test each one.

Test all three paths. A good programmer tests every path through nested code, not just the happy one. When you type the wrong username, watch how the whole inner block fades out. Python skipped it without checking anything inside.

Nested Conditionals

Nesting a whole if-elif-else

The inner part of a nested if can be any kind of conditional, including a full if-elif-else chain.

In this game, the player needs a key before anything else matters. If they have the key, their level decides which door opens.

Try it: level 12 with the key, level 7 with the key, level 2 with the key, and then level 12 without the key. Without the key, the level is never checked.
ranskipped

Read it as two separate decisions

  1. Outer question: does the player have the key? If not, nothing else matters.
  2. Inner question (only with the key): which level range are they in? Exactly one door opens, because it is an if-elif-else chain.

Counting the paths: 3 inner choices plus 1 "no key" path makes 4 possible outcomes. That is why the slide asks for four tests.

Indentation check: the inner if, elif, and else all line up with each other (4 spaces). Their bodies are at 8 spaces. The outer else is back at 0.

  • The inner block can be if, if-else, or if-elif-else.
  • Line up elif/else with the if they belong to.
  • Count paths: inner choices plus the outer else.

Here the outer question is: do you have the key? Only then do we check the level, and that inner check is a whole elif chain. Look at the indentation: the inner if, elif, and else all line up with each other, 4 spaces in. Their bodies are 8 spaces in.

Nested Conditionals

Nested if or and?

These two programs do the same thing:

Nested
if has_ticket:
    if age >= 13:
        print("Enjoy the movie!")
Combined with and
if has_ticket and age >= 13:
    print("Enjoy the movie!")

When you only care if both are True, the and version is shorter and easier to read. But nesting is the better choice when each "no" needs its own message:

if has_ticket:
    if age >= 13:
        print("Enjoy the movie!")
    else:
        print("This movie is PG-13. Pick another one.")
else:
    print("Please buy a ticket first.")
Rule of thumb: same result for every "no"? Use and. Different result depending on which check failed? Use nesting.

How they turn into each other

An outer if whose only content is an inner if (and no else anywhere) can always be flattened: just join the two conditions with and. Once you add an else with its own message, flattening would lose information, so nesting is the clearer choice.

Too deep? If you find yourself at 4 or 5 levels of nesting, step back. Often you can combine some checks with and, or store parts in well-named Boolean variables, to flatten it.
  • Nested if with no else = and.
  • Nesting lets you give a different response for each failed check.
  • Avoid nesting more than 2 or 3 levels deep. It gets hard to read.
  • No else anywhere: nested ifs can become one and.

Many nested ifs can be rewritten with and. So which is better? It depends. If the user needs to know why they were turned away, nesting lets you give a specific message for each reason. If you just need yes or no, and is cleaner.

Nested Conditionals

Which if owns the else?

In nested code, an else belongs to the if it lines up with. Moving an else 4 spaces changes the whole meaning. Both programs use x = 3.

else lines up with the inner if
x = 3
if x > 0:
    if x > 5:
        print("big")
    else:
        print("small")
small

3 > 0 is True, so Python goes inside. 3 > 5 is False, so the inner else runs.

else lines up with the outer if
x = 3
if x > 0:
    if x > 5:
        print("big")
else:
    print("negative or zero")
(nothing prints)

3 > 0 is True, so the outer else is skipped. 3 > 5 is False, and the inner if has no else. Nothing prints.

Tracing tip: put your finger on the else and slide it straight up until you hit an if at the same indentation. That is its partner.

Python decides by indentation only

Python never guesses based on what makes sense. An else belongs to whichever if has exactly the same indentation above it, with nothing at a smaller indentation in between. Moving it 4 spaces gives it a new partner and a new meaning.

Try x = -2 in both programs

  • Left: -2 > 0 is False, so the whole outer body (including the inner else) is skipped. Nothing prints.
  • Right: -2 > 0 is False, so the outer else runs: negative or zero.
  • An else pairs with the if at the same indentation.
  • Wrong indentation can cause a logic error with no error message.
  • An else pairs with the if at its exact indentation.

These two programs look almost identical. The only difference is four spaces before the else. On the left, the else belongs to the inner if. On the right, it belongs to the outer if, and nothing prints at all. Use the finger trick to match each else with its if.

Debugging

Common mistakes

MistakeWrong codeWhat happensFix
Half a comparison after orif color == "red" or "blue":No error. Always True (logic error).color == "red" or color == "blue"
Missing variable after andif x > 5 and < 10:SyntaxError: invalid syntaxx > 5 and x < 10
Using symbols from other languagesif a && b: or if a || b:SyntaxError: invalid syntaxand, or
Capital lettersif a AND b:SyntaxError: invalid syntaxand, or, not are lowercase
Inner body not indented furtherif x > 3:
    if x > 4:
    print("a")
IndentationError: expected an indented block after 'if' statement on line 3Indent the inner body 8 spaces
else at an in-between indentation  else: (2 spaces)IndentationError: unindent does not match any outer indentation levelLine else up exactly with its if
else lined up with the wrong ifsee the previous slideNo error. Wrong path runs (logic error).Trace which if each else belongs to
Missing parenthesesh >= 48 and a >= 10 or adultNo error. and is grouped first (logic error).h >= 48 and (a >= 10 or adult)

Error messages shown are from Python 3.13. Older versions word some messages a little differently. Notice that half of these mistakes give no error at all. That is why testing with many different values is so important.

How to find a logic error

  1. Write down what should happen for a few inputs before you run anything.
  2. Run each input and compare. The first one that does not match tells you which path is broken.
  3. Step through (or add a print() of the condition, like print(color == "red" or "blue")) to see exactly what Python calculated.
  4. Check the usual suspects: half comparisons, missing parentheses, and else at the wrong level.
  • Syntax errors stop the program. Logic errors run but give wrong answers.
  • Test every path, including values right on the edge.
  • Print the condition itself to see what Python calculated.

Look at the What Happens column. Half of these mistakes give no error message. Python is happy to run them, and they just give the wrong answer. Those are the dangerous ones. The only way to catch them is to test your program with lots of different values.

Practice

Check yourself

Predict the answer, then click Check. This is practice only. Nothing is graded or recorded.

For each question:
  • Replace every comparison with True or False first.
  • Then do not, then and, then or (parentheses first if there are any).
  • For nested code, match each else to its if with your finger before you trace.
  • Remember the order: comparisons, not, and, or.
  • Match each else to the if it lines up with.

Work these out on paper first. For the expression questions, write out each step like the evaluator showed you. For the nested questions, trace one line at a time and keep track of which bodies Python enters.

Summary

Cheat sheet

Complex conditions

a and b     # True if both are True
a or b      # True if at least one is
not a       # flips True / False
1 <= x <= 10   # x is from 1 to 10

Order: ( ), math, comparisons, not, and, or.

Multiple ifs

if temp < 60:
    print("jacket")
if is_raining:
    print("umbrella")

Independent questions. Every if is checked, so several can run.

Nested if

if has_key:
    if level >= 5:
        print("Door opens")
    else:
        print("Level up")
else:
    print("Find the key")

A decision inside a decision. Each level adds 4 spaces.

Before you run your code, check:

  • Each side of and / or is a complete comparison
  • Parentheses group the parts that belong together
  • and, or, not, True, False are spelled correctly (only True/False are capitalized)
  • Each else lines up with the if it belongs to
  • Separate ifs for independent questions, elif when only one choice should happen
  • You tested every path, including edge values

Key words to know

Boolean expression · and / or / not · truth table · precedence · short-circuit evaluation · chained comparison (1 <= x <= 10) · nested if · logic error · testing every path

  • and: both. or: at least one. not: flip.
  • Separate ifs: all checked. Nested ifs: inner only when outer allows.
  • Know the key words: truth table, precedence, short-circuit, nested if.

Let's wrap up. And needs both, or needs one, not flips it. Use parentheses to make your meaning clear. Use separate ifs for independent questions and nested ifs for decisions that depend on earlier decisions. Before you run any code, go down this checklist.