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→ next · ← back · n notes · s script · f full
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 separateif statements.
Write nestedif 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
Word
What it means
Boolean expression
Anything that works out to True or False, like age >= 13
complex condition
Two or more conditions joined with and / or, possibly with not
truth table
A chart showing the result for every possible True/False combination
precedence
The order Python uses to work out an expression
nested if
An if written inside the body of another if or else
logic error
A 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.
Operator
Meaning
Example
Result
==
equal to
4 == 4
True
!=
not equal to
4 != 4
False
><
greater / less than
9 < 2
False
>=<=
or equal to
7 >= 7
True
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
A
not A
True
False
False
True
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:
Order
What
Example
1st
Parentheses
( )
2nd
Math
+ - * / %
3rd
Comparisons
== != < > <= >=
4th
not
5th
and
6th
or
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 olderor 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 ifs
each question is independent, and more than one can be True
if-elif-else
only 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
age < 13 or age >= 65: False or True is True, so the price is $8.
The rest of the chain is skipped.
Trace age 16, a student
age < 13 or age >= 65: False or False is False. Move on.
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 nestedif 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:
First: does this username exist?
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.
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
Username
Password
Expected path
coder1
py123
outer True, inner True: Welcome back!
coder1
wrong
outer 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
Outer question: does the player have the key? If not, nothing else matters.
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 innerelse 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
Mistake
Wrong code
What happens
Fix
Half a comparison after or
if color == "red" or "blue":
No error. Always True (logic error).
color == "red" or color == "blue"
Missing variable after and
if x > 5 and < 10:
SyntaxError: invalid syntax
x > 5 and x < 10
Using symbols from other languages
if a && b: or if a || b:
SyntaxError: invalid syntax
and, or
Capital letters
if a AND b:
SyntaxError: invalid syntax
and, or, not are lowercase
Inner body not indented further
if x > 3: if x > 4: print("a")
IndentationError: expected an indented block after 'if' statement on line 3
Indent the inner body 8 spaces
else at an in-between indentation
else: (2 spaces)
IndentationError: unindent does not match any outer indentation level
Line else up exactly with its if
else lined up with the wrong if
see the previous slide
No error. Wrong path runs (logic error).
Trace which if each else belongs to
Missing parentheses
h >= 48 and a >= 10 or adult
No 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
Write down what should happen for a few inputs before you run anything.
Run each input and compare. The first one that does not match tells you which path is broken.
Step through (or add a print() of the condition, like print(color == "red" or "blue")) to see exactly what Python calculated.
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. Everyif 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.