A community calendar needs to decide whether the given year 2024 gets a leap day. The finished program should print one exact message:
2024 is a leap year.How can a program choose the right message instead of printing every possibility?
A leap year is divisible by 4, except a century year is not a leap year, except every 400th year is a leap year after all. That explains why 1900 is not a leap year, while 2000 and 2024 are leap years. In Python, the full test is
(year % 4 == 0 and year % 100 != 0) or year % 400 == 0.Run the single given year below.
Preview — you will build every part of this by the end of the unit.
year = 2024 is_leap_year = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 if is_leap_year: print(f"{year} is a leap year.") else: print(f"{year} is not a leap year.")Scroll the code for more ↓
Output
2024 is a leap year.The boolean test becomes
Truefor 2024, so only the first indented message runs. We will build this payoff in three lessons. Each idea climbs the same ladder: minimal, one twist, then realistic.-
Choose when code runs with
if,elif, andelse. -
Combine yes-or-no tests with
and,or, andnot. -
Put one decision inside another and order the branches so the right one runs.
-
Lesson 1: Choose with
ifA one-way decision
An
ifstatement runs its indented block only when its condition isTrue. A colon ends the condition line.Minimal: print a message when a score reaches a target.
score = 92 if score >= 90: print("Target reached!")Scroll the code for more ↓
Output
Target reached!The indented line belongs to the decision. It runs because
92 >= 90isTrue.One twist: a false condition skips its block.
temperature = 65 if temperature > 80: print("Bring water.") print("Check complete.")Scroll the code for more ↓
Output
Check complete.The indented reminder is skipped, but the unindented final line runs.
Realistic: use an equality test to recognize an exact score. A common broken version uses assignment inside the condition.
Error demo: this code stops with an error score = 90 if score = 90: print("Exact score!")Scroll the code for more ↓
The traceback ends with
SyntaxErrorbecause=saves a value; it does not ask an equality question. Use==in the condition.score = 90 if score == 90: print("Exact score!")Scroll the code for more ↓
Output
Exact score!if score == 90:asks a question and ends with a colon. The repaired program printsExact score!.Two possible paths
An
elseblock supplies the path used when theifcondition isFalse.Minimal: choose between two messages.
has_ticket = True if has_ticket: print("Enter") else: print("Stop")Scroll the code for more ↓
Output
EnterExactly one branch runs: the
ifbranch forTrue, or theelsebranch forFalse.One twist: the condition can compare a saved number.
items = 7 if items % 2 == 0: print("Even group") else: print("Odd group")Scroll the code for more ↓
Output
Odd groupThe remainder decides which of the two labels is printed.
Realistic: include the given value in the chosen output.
balance = 12 if balance >= 10: print(f"Balance {balance}: ready") else: print(f"Balance {balance}: add funds")Scroll the code for more ↓
Output
Balance 12: readyBoth branches use the same fixed value, but only the matching message appears.
Real input: try the two-path score decision with a score supplied by a person.
Try it score = int(input("Score: ")) if score >= 10: print("Ready") else: print("Keep practicing")Scroll the code for more ↓
int()converts the typed score before the comparison, and only one message prints.Lesson 2: Choose among Several Paths
Logical operators
andisTrueonly when both joined tests are true.orisTruewhen at least one joined test is true.notreverses a boolean:TruebecomesFalse, andFalsebecomesTrue.Minimal: join two booleans with
and.print(True and False)Scroll the code for more ↓
Output
FalseandprintsFalsebecause both booleans must beTrue.One twist: use
orso one true boolean is enough.print(True or False)Scroll the code for more ↓
Output
TrueorprintsTruebecause its first boolean isTrue.Minimal: reverse one boolean.
print(not True)Scroll the code for more ↓
Output
Falsenot TrueprintsFalsebecausenotflips the boolean.One twist: use
notin a condition.has_ticket = False if not has_ticket: print("Ticket needed")Scroll the code for more ↓
Output
Ticket neededhas_ticketisFalse, sonot has_ticketisTrueand the indented message runs.Another twist: check that a value is in an inclusive range.
x = 7 in_range = x >= 0 and x <= 10 print(in_range)Scroll the code for more ↓
Output
Truex >= 0 and x <= 10makes both boundary tests visible. The chained form0 <= x <= 10asks the same inclusive-range question.One twist: write the same inclusive range as a chained comparison.
x = 7 print(0 <= x <= 10)Scroll the code for more ↓
Output
TrueThe chained comparison prints
Truebecause 7 is between both endpoints.Realistic: accept either of two exact values.
day = "Saturday" if day == "Saturday" or day == "Sunday": print("Weekend") else: print("Weekday")Scroll the code for more ↓
Output
WeekendOne true equality is enough for the
orcondition to chooseWeekend.One twist: compare two strings that differ only in capitalization.
print("Yes" == "yes")Scroll the code for more ↓
Output
FalseEquality is case-sensitive, so
"Yes" == "yes"printsFalse.Real input: try the weekend decision with a day supplied by a person.
Try it day = input("Day: ") if day == "Saturday" or day == "Sunday": print("Weekend") else: print("Weekday")Scroll the code for more ↓
This cell asks for a day when you run it yourself; the decision still prints one label.
elifruns top-downAn
elifadds another condition betweenifandelse. Python checks the conditions from top to bottom, then runs the first matching branch and skips the rest.Minimal: classify a score with the highest threshold first.
score = 95 if score >= 90: print("Level A") elif score >= 80: print("Level B") else: print("Keep practicing")Scroll the code for more ↓
Output
Level A95 also satisfies
score >= 80, but thescore >= 90branch matches first. Python prints onlyLevel A. Order matters.One twist: add another threshold while keeping the checks highest-to-lowest.
score = 84 if score >= 90: print("Gold") elif score >= 80: print("Silver") elif score >= 70: print("Bronze") else: print("Practice round")Scroll the code for more ↓
Output
Silver84 misses the first test and matches the second, so later branches are not checked.
One twist: an
elifcan require two tests at once withand.score = 84 if score >= 90: print("Gold") elif score >= 80 and score < 90: print("Silver") else: print("Practice round")Scroll the code for more ↓
Output
SilverBoth parts of the
elifare true for 84, so the Silver branch runs. The< 90half is already guaranteed by the top-down order — it is written here only to showandinside anelif; in your own ladders, leave it out.Realistic: a triangle label must check the most specific equality pattern first.
side_a = 5 side_b = 5 side_c = 5 if side_a == side_b and side_b == side_c: print("Equilateral") elif side_a == side_b or side_a == side_c or side_b == side_c: print("Isosceles") else: print("Scalene")Scroll the code for more ↓
Output
EquilateralAn equilateral triangle also has pairs of equal sides. Checking equilateral first prevents the broader isosceles branch from taking it. The
elifcondition usesor: any one of its three equal-side tests is enough.Lesson 3: Build Clear Decision Trees
Nested decisions
A decision can contain another decision. The extra indentation shows that the inner question is asked only inside the chosen outer branch.
Minimal: check permission before checking a score.
has_permission = True score = 8 if has_permission: if score >= 7: print("Activity unlocked")Scroll the code for more ↓
Output
Activity unlockedThe score test is reached only after permission is confirmed.
One twist: give each decision its own alternative.
has_permission = True score = 5 if has_permission: if score >= 7: print("Activity unlocked") else: print("Raise the score") else: print("Permission needed")Scroll the code for more ↓
Output
Raise the scoreEach
elselines up with the condition it belongs to. Here permission is present, so the innerelseprints.Realistic: use fixed stand-in text in a nested choice.
age_text = "12" age = int(age_text) has_pass = True if age >= 10: if has_pass: print("Entry approved") else: print("Pass needed") else: print("Age requirement not met")Scroll the code for more ↓
Output
Entry approvedThe fixed text
"12"becomes an integer before the outer decision. Then the inner decision checks the pass.Real input: try the nested entry decision with an age supplied by a person.
Try it age_text = input("Age: ") age = int(age_text) has_pass = True if age >= 10: if has_pass: print("Entry approved") else: print("Pass needed") else: print("Age requirement not met")Scroll the code for more ↓
The typed age becomes an integer before the outer decision; the inner decision runs only when the age qualifies.
Return to the leap-year rule
The calendar rule joins two paths: a year passes when it is divisible by 4 and not divisible by 100, or when it is divisible by 400. Parentheses keep the first pair together.
Minimal: check whether one year is divisible by 4.
year = 2024 print(year % 4 == 0)Scroll the code for more ↓
Output
True2024 has no remainder after division by 4, so the comparison prints
True.One twist: exclude centuries from the divisible-by-4 rule.
year = 2024 print(year % 4 == 0 and year % 100 != 0)Scroll the code for more ↓
Output
TrueBoth tests are true for 2024, so this partial rule prints
True.One twist: include years divisible by 400 again.
year = 2024 print((year % 4 == 0 and year % 100 != 0) or year % 400 == 0)Scroll the code for more ↓
Output
TrueThe complete rule prints
True; theoradds the every-400th-year exception. Whenandandorshare a condition,andbinds tighter thanor; use parentheses to show the intended groups.Minimal: save the boolean for one given year.
year = 2024 is_leap_year = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 print(is_leap_year)Scroll the code for more ↓
Output
TrueThe full test prints
Truefor the single given year 2024.One twist: use the saved boolean to choose one exact label.
year = 2024 is_leap_year = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 if is_leap_year: label = "leap year" else: label = "common year" print(label)Scroll the code for more ↓
Output
leap yearThe calculation and the message choice have separate jobs, which makes the rule easier to read.
Realistic: print the full calendar message from the opening.
year = 2024 is_leap_year = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 if is_leap_year: print(f"{year} is a leap year.") else: print(f"{year} is not a leap year.")Scroll the code for more ↓
Output
2024 is a leap year.One condition controls two possible messages, and exactly one appears.
Real input: try the full calendar decision with a year supplied by a person.
Try it year = int(input("Year: ")) is_leap_year = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 if is_leap_year: print(f"{year} is a leap year.") else: print(f"{year} is not a leap year.")Scroll the code for more ↓
Converting the typed year lets the same full rule choose one calendar sentence.
Final build
Run the calendar decision with the fixed year 2024. Read the condition aloud: divisible by 4 and not by 100, or divisible by 400.
# One given year; no repeated testing is needed. year = 2024 is_leap_year = (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 if is_leap_year: print(f"{year} is a leap year.") else: print(f"{year} is not a leap year.")Scroll the code for more ↓
Output
2024 is a leap year.Comparisons create booleans, logical operators combine them, and the decision selects the one matching output.
-
ifruns a block when its condition is true. -
elifchecks another path;elsehandles what remains. -
and,or, andnotcombine or reverse yes-or-no tests. -
An indented decision inside another decision asks a second question.
-
Branch order matters because Python takes the first matching path.
You can now build a calendar decision that identifies a leap year.
-