Free Study Material
Every program you've written so far ran one line after another, top to bottom, with no choices along the way. Real programs need to make decisions — this chapter starts that, beginning with how Python marks out which lines belong together.
Control flow is the order in which a program's statements actually execute. Python has three basic types:
Python uses indentation — leading whitespace — to mark which lines belong to the same block, instead of curly braces {} like many other languages. Every statement inside the same block must be indented by the exact same amount.
marks = 75
if marks >= 40:
print("You have passed.")
print("Well done.")
print("Result processing complete.")
The two print() lines directly under if marks >= 40: are indented identically, so Python treats them as one block belonging to that if. The final print() is back at the original indentation level, so it is not part of the if block — it runs regardless of whether marks >= 40 is True or False.
Warning: Mixing tabs and spaces, or indenting two lines of the same block by different amounts, raises an IndentationError — Python treats consistent indentation as part of the code's actual meaning, not just a style choice.
This is the single biggest syntax difference a student coming from a language that uses { } needs to unlearn: in Python, indentation is the block structure. There is no closing symbol to mark where a block ends — it ends the moment a line returns to a shallower indentation level.
Control flow is the order statements execute in: sequential (top to bottom), selection (choosing a path), and iteration (repeating a block). Python marks out blocks using consistent indentation instead of braces — every statement in the same block must be indented by exactly the same amount.
By default, Python runs code Sequentially (top to bottom). But what if you only want to run a line IF the user is over 18? Or what if you want to repeat a task 100 times? This is Flow of Control. We use logic to divert the "path" of the program.
All statements in a single "block" must have the same level of indentation. Usually, this is 4 spaces or 1 Tab. If you get this wrong, Python will throw an IndentationError.
Line 1, then Line 2, then Line 3. Standard flow.
Deciding which path to take using if, elif, and else.
Repeating a block of code using for or while.
The simplest kind of selection is a single yes/no check: run a block of code only if a condition is true, otherwise skip it entirely. That's exactly what Python's if statement does.
if condition:
statement(s)
condition is any expression that evaluates to True or False. If it's True, the indented block runs. If it's False, the block is skipped completely, and the program continues with whatever comes after it.
temperature = 40
if temperature > 35:
print("Heatwave warning issued.")
print("Weather check complete.")
temperature > 35 evaluates to True (40 > 35), so the warning prints. Then "Weather check complete." prints regardless, because it's outside the if block. If temperature were 30 instead, the warning would be skipped entirely, and only "Weather check complete." would print.
Note: Anifwith no matchingelsesimply does nothing when its condition isFalse— it does not raise an error, and it does not stop the program.
An if block is only ever entered when its condition is exactly True — not "truthy-looking," not a near match. temperature > 35 must genuinely evaluate to the Boolean True for the block to run, which is why writing the condition correctly (using the right relational or logical operator from the last chapter) matters as much as writing the block itself.
if condition: runs its indented block only when condition evaluates to True; when it's False, the block is skipped entirely and the program continues after it, with no error raised either way.
The if statement is used for Selection. It allows the program to test a condition: if the condition is True, the program "selects" a specific block of code to run. If the condition is False, the program simply ignores that block. This is how we make programs "smart" enough to respond to different data.
The structure of a simple if follows a specific "grammar" that Python requires:
if.x > 5) that gives True or False.
# Program to demonstrate the simple if statement
score = 94
if score >= 90:
print("Excellent work!")
print("You qualify for the KV Coders Scholarship.")
print("Please collect your certificate.")
print("Program execution complete.")
| SyntaxError | Forgetting the : at the end of the if line. |
| IndentationError | Not indenting the statements inside the if block. |
| Logic Error | Using = (assignment) instead of == (comparison). |
A plain if only handles one path — do something, or do nothing. Most real decisions need a second path for when the condition is false, which is what else adds.
if condition:
statement(s) run when condition is True
else:
statement(s) run when condition is False
Exactly one of the two blocks runs, every time — never both, and never neither.
marks = 32
if marks >= 40:
print("Result: PASS")
else:
print("Result: FAIL")
marks >= 40 is False (32 is less than 40), so the if block is skipped and the else block runs instead, printing Result: FAIL. If marks were 55, the if block would run and print Result: PASS, and the else block would be skipped.
Note:elsenever has its own condition — it simply means "whenever theifabove it was False." Writingelse ifis not valid Python syntax at all (the correct way to check another condition is covered in the next topic).
Because exactly one branch always runs, if-else is the right structure whenever a decision genuinely has two outcomes and every case must be handled — unlike a plain if, which is only appropriate when doing nothing is itself an acceptable outcome.
if-else runs exactly one of two blocks: the if block when its condition is True, or the else block when it's False — one of the two always executes, and else never takes its own condition.
The if-else statement provides two paths for the program. Unlike the simple if, which either runs code or does nothing, if-else ensures that exactly one of the two blocks will execute. If the condition is True, the first block runs; otherwise, the else block runs.
Note that the else keyword never takes a condition. It is the "catch-all" for when the if fails.
# Program to check if a number is Even or Odd
num = 15
if num % 2 == 0:
print(num, "is an Even number.")
print("It is divisible by 2.")
else:
print(num, "is an Odd number.")
print("It leaves a remainder when divided by 2.")
print("Calculation finished.")
if-else only chooses between two outcomes. Many real decisions — like assigning a grade from a mark — have more than two possible outcomes. The if-elif-else ladder handles exactly that.
if condition1:
statement(s)
elif condition2:
statement(s)
elif condition3:
statement(s)
else:
statement(s)
Python checks each condition top to bottom and runs the block belonging to the first one that is True — every condition below it is skipped, even if it would also have been True. If none of the conditions are True, the final else block runs. elif (short for "else if") can repeat as many times as needed; else is optional, and always goes last.
marks = 78
if marks >= 90:
print("Grade: A")
elif marks >= 75:
print("Grade: B")
elif marks >= 60:
print("Grade: C")
else:
print("Grade: D")
Python checks marks >= 90 first — False (78 is less than 90). It then checks marks >= 75 — True (78 is at least 75) — so Grade: B prints, and every condition below it (marks >= 60, and the else) is skipped entirely, even though marks >= 60 would also have been True.
Warning: Ordering matters. If the conditions above were written from lowest to highest instead (marks >= 60checked first), a mark of95would incorrectly printGrade: C, because the first matching condition wins and the ladder never continues checking after that. Write ladders like this from the highest boundary down.
"First match wins, then stop" is the one rule this entire structure depends on — it's exactly why the boundary marks in a grading ladder must be ordered from strictest to loosest, top to bottom, and not the other way around.
if-elif-else checks each condition in order and runs only the block belonging to the first one that's True, skipping every condition after it — a ladder handling boundary-based decisions (like grading) must be ordered from the strictest condition to the loosest, top to bottom.
Sometimes we need to check multiple conditions in a sequence. The if-elif-else ladder allows Python to check conditions one by one from top to bottom. As soon as it finds a True condition, it executes that block and skips the rest of the ladder. The elif is short for "else if."
You can have as many elif blocks as you need, but you can only have one if and one else.
# Program to calculate Grade based on Marks
marks = 65
if marks >= 90:
print("Grade: A+ (Outstanding)")
elif marks >= 80:
print("Grade: A (Excellent)")
elif marks >= 70:
print("Grade: B (Good)")
elif marks >= 33:
print("Grade: C (Pass)")
else:
print("Grade: F (Fail/Re-appear)")
print("Evaluation Complete.")
| Situation | Best Structure |
| One choice (Yes or do nothing) | if |
| Two choices (Either A or B) | if-else |
| Multiple choices (Select one of many) | if-elif-else |
Sometimes a second decision only makes sense once a first condition is already known to be true — like checking a specific eligibility rule, but only for students who already passed. Placing an if inside another if handles exactly that.
A nested if is an if (or if-else) statement placed inside the block of another if (or else). The inner statement only runs at all if the outer condition was True first. Each nested level needs its own, further level of indentation.
marks = 85
attendance = 78
if marks >= 40:
print("Result: PASS")
if attendance >= 75:
print("Eligible for merit certificate.")
else:
print("Not eligible for merit certificate (attendance below 75%).")
else:
print("Result: FAIL")
marks >= 40 is True, so Result: PASS prints, and Python enters that block — which is where the nested if attendance >= 75: lives. attendance >= 75 is also True (78 is at least 75), so Eligible for merit certificate. prints too. If marks were below 40 instead, the entire nested if-else would never even be checked — the outer else would run, printing only Result: FAIL.
Note: The nestedif-elseis indented one further level than the outerif's own block — itsif/elsekeywords must line up with each other, and everything inside them is indented one level deeper still.
Nesting exists specifically for decisions that are only meaningful given an outer condition — checking attendance for a merit certificate is pointless for a student who hasn't even passed, which is exactly why that check belongs inside the PASS branch rather than as a separate, independent if.
A nested if is an if/if-else placed inside another if's block, and it only runs when the outer condition was already True — each nested level requires its own further indentation, keeping the block structure that decides which lines depend on which condition unambiguous.
A Nested if occurs when an if statement is placed inside the block of another if statement. This is used when a second condition needs to be checked only after the first condition is already found to be True.
# Change this value to test different logic paths
num = 14
if num >= 0:
print("The number is Positive.")
# Nested check: only happens if num is positive
if num % 2 == 0:
print("Specifically, it is an Even number.")
else:
print("Specifically, it is an Odd number.")
else:
print("The number is Negative.")