Warming up the neural circuits...
By the end of this chapter you will:
Programs earn their keep by making decisions. Without conditionals, every program would do the same thing every time — no login checks, no error handling, no dynamic behavior. Conditionals let your code say "if this is true, do X; otherwise, do Y."
Python's conditionals are clean and readable. By the end of this chapter, you'll write branching logic that's easy to understand and maintain.
Python gives you three tools for making decisions. Click each to see how to use it:
| Operator | Meaning | Example |
|---|---|---|
== | Equal to | 5 == 5 → True |
!= | Not equal to | 5 != 3 → True |
> | Greater than | 5 > 3 → True |
< | Less than | 3 < 5 → True |
>= | Greater than or equal | 5 >= 5 → True |
<= | Less than or equal | 3 <= 5 → True |
Chaining comparisons:
age = 25
print(18 <= age < 65) # True (equivalent to 18 <= age and age < 65)Key insight: Python chains comparisons like math notation — more readable than age >= 18 and age < 65.
Falsy values (considered false):
| Type | Falsy values |
|---|---|
| bool | False |
| None | None |
| int | 0 |
| float | 0.0 |
| str | "" (empty string) |
| list | [] (empty list) |
| dict | {} (empty dict) |
| tuple | () (empty tuple) |
Truthy values: Everything else — non-zero numbers, non-empty strings, non-empty collections.
name = ""
if name:
print(f"Hello, {name
age = 25
has_id = True
if age >= 18 and has_id:
print("Entry allowed")
day = "Saturday"
if day == "Saturday" or day == "Sunday":
print("
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True (same values)
print(a is b) # False (different objects)
c = a
Think of conditionals as a flowchart:
Start → Check condition → True → Do X
→ False → Check next → True → Do Y
→ False → Do ZEach if/elif/else is a branch in the flowchart.
The basic structure:
age = 20
if age >= 18:
print("You can vote")
elif age >= 16:
print("You can drive")
else:
print("You're too young")Key facts:
if checks a condition. If it's true, the indented block runs.elif (else if) checks another condition if the previous ones were false.else runs if all previous conditions were false.Multiple conditions:
score = 85
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
elif score >= 60:
grade = "D"
No else required:
temperature = 30
if temperature > 35:
print("It's hot!")
# No else — if temperature <= 35, nothing happens| Operator | Meaning | Example |
|---|---|---|
== | Equal to | 5 == 5 → True |
!= | Not equal to | 5 != 3 → True |
> | Greater than | 5 > 3 → True |
< | Less than | 3 < 5 → True |
>= | Greater than or equal | 5 >= 5 → True |
<= | Less than or equal | → |
Chaining comparisons:
Python lets you chain comparisons like math notation:
age = 25
print(18 <= age < 65) # True (equivalent to 18 <= age and age < 65)
score = 75
print(60 <= score <= 80) # TrueThis is more readable than age >= 18 and age < 65.
In Python, every value has a "truthiness" — whether it's considered true or false in a boolean context (like an if condition).
Falsy values (considered false):
FalseNone0 (int)0.0 (float)"" (empty string)[] (empty list){} (empty dict)() (empty tuple)set() (empty set)Truthy values (considered true):
Examples:
name = ""
if name:
print(f"Hello, {name}")
else:
print("Name is empty") # This runs
numbers = [1, 2, 3]
if numbers:
print
When to be explicit:
Truthiness is concise, but sometimes explicit is clearer:
# Concise (truthiness)
if users:
process(users)
# Explicit (clearer intent)
if len(users) > 0:
process(users)
# Explicit (checking for None)
if result is not None:
process(result)Use truthiness for simple checks. Use explicit comparisons when the intent is unclear or when you need to distinguish between 0 and None.
Combine conditions with logical operators:
and — both conditions must be true:
age = 25
has_id = True
if age >= 18 and has_id:
print("Entry allowed")or — at least one condition must be true:
day = "Saturday"
if day == "Saturday" or day == "Sunday":
print("It's the weekend")not — inverts the condition:
is_raining = False
if not is_raining:
print("Let's go outside")Precedence:
not > and > or
# This:
if a or b and c:
pass
# Is equivalent to:
if a or (b and c):
passUse parentheses to make intent clear:
if (a or b) and c:
passShort-circuit evaluation:
and and or short-circuit — they stop evaluating as soon as the result is determined:
# and: if the first condition is false, the second isn't evaluated
if user and user.is_authenticated:
# If user is None, user.is_authenticated isn't called (no AttributeError)
pass
# or: if the first condition is true, the second isn't evaluated
name = user_input or "default"
# If user_input is truthy, "default" isn't evaluatedReturning values:
and and or return one of the operands, not necessarily True or False:
# and returns the first falsy value, or the last value if all are truthy
print(True and "hello") # 'hello'
print(False and "hello") # False
print("a" and "b") # 'b'
This is useful for defaults:
username = input("Enter username: ") or "guest"
# If input is empty (falsy), username becomes "guest"== checks if two objects have the same value:
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True (same values)is checks if two names refer to the same object in memory:
a = [1, 2, 3]
b = [1, 2, 3]
print(a is b) # False (different objects)
c = a
print(a is c) # True (same object)When to use which:
== to compare values (most cases).is to check for None or singletons:result = None
if result is None: # Correct
pass
if result == None: # Works, but not idiomatic
passWhy it matters:
# Small integers are cached (same object)
a = 256
b = 256
print(a is b) # True (same cached object)
# Large integers are not cached
a = 257
b = 257
print(a is b) # False (different objects)Don't rely on integer caching — use == for value comparisons.
:
def validate_user(username, password):
if not username:
return "Username is required"
if len(password) < 8:
return "Password must be at least 8 characters"
if "@" not in username:
return "Username must be an email"
machines:
status = "pending"
if status == "pending":
action = "Approve or reject"
elif status == "approved":
action = "Process payment"
elif status == "rejected":
action = "Notify user"
else
Guard clauses:
def process_order(order):
if not order:
return # Early exit
if order.is_cancelled:
return # Early exit
# Main logic
charge_payment(order)
ship_items(order)| Mistake | Why it's wrong | Fix |
|---|---|---|
if x = 5: → SyntaxError | = is assignment, == is comparison. | Use == for comparison: if x == 5: |
if x: when you meant if x is not None: | if x: is false for 0, "", [], etc. — not just None. | Use if x is not None: when you specifically want to check for None. |
if x == None: | is a singleton. Use for identity checks. |
Ternary operator:
Python has a one-line conditional expression:
status = "adult" if age >= 18 else "minor"Equivalent to:
if age >= 18:
status = "adult"
else:
status = "minor"Use ternary for simple assignments. For complex logic, use if/else blocks.
Pattern matching (Python 3.10+):
Python 3.10 introduced match/case for complex conditionals:
match status:
case "pending":
action = "Approve or reject"
case "approved":
action = "Process payment"
case "rejected":
action = "Notify user"
case _:
action = "Unknown status"We'll cover pattern matching in L1.
Performance:
Conditionals are fast. Don't worry about optimizing them unless you're in a tight loop. For complex conditions, extract them into functions for readability:
def is_valid_order(order):
return order and order.items and order.total > 0
if is_valid_order(order):
process(order)x = 0; if x: print("Truthy") else: print("Falsy") print? Why?== and is? When would you use each?Beginner:
"What's the difference between if x: and if x == True:?"
if x:checks ifxis truthy (non-zero, non-empty, notNone).if x == True:checks ifxis exactly equal toTrue. For most values, they're equivalent, butif x:is more Pythonic and handles more cases (e.g., non-empty strings, non-zero numbers).
"What does and return in Python?"
andreturns the first falsy value, or the last value if all are truthy. For example, returns , and returns .
if/elif/else for branching. Only one block runs — the first true condition.==, !=, >, <, >=, <=. Chain them: 18 <= age < 65.0, "", [], {}, None are falsy. Everything else is truthy.and/or/not combine conditions. and requires both, requires one, inverts.I want to…
├── Branch on a condition → if/elif/else
├── Compare values → ==, !=, >, <, >=, <=
├── Combine conditions → and, or, not
├── Check for None → if x is None:
├── Check if empty → if not items:
├── Chain comparisons → 18 <= age < 65
└── Conditional assignment → x = "yes" if condition else "no"What's the difference between 'if x:' and 'if x == True:'?
When to use: Simple existence checks. When to be explicit: When intent is unclear or you need to distinguish 0 from None.
Precedence: not > and > or
# This:
if a or b and c:
pass
# Is equivalent to:
if a or (b and c):
passShort-circuit evaluation: and and or stop as soon as the result is determined.
When to use is:
None: if result is None:if x is True:When to use ==:
if age == 25:if name == "Alice":Warning: Don't rely on is for integers > 256 — Python caches small integers.
3 <= 5Trueisif x is None: |
if a or b == c: | Precedence: b == c is evaluated first, then a or (result). | Use parentheses: if (a or b) == c: or if a or b == c: depending on intent. |
Forgetting indentation after if | Python uses indentation to define blocks. | Indent the block with 4 spaces. |
Using elif without if | elif must follow an if or another elif. | Start with if. |
True and "hello""hello"False and "hello"FalseSenior:
"What's the difference between == and is in Python?"
==checks value equality (do these objects have the same content?).ischecks identity (are these the same object in memory?). Use==for value comparisons,isfor identity checks (especially withNone).
"What is short-circuit evaluation? Give an example where it matters."
Short-circuit evaluation means
andandorstop evaluating as soon as the result is determined. Forand, if the first operand is false, the second isn't evaluated. Foror, if the first operand is true, the second isn't evaluated. Example:if user and user.is_authenticated:— ifuserisNone,user.is_authenticatedisn't called, preventing anAttributeError.
not== checks value equality, is checks identity. Use is for None.x if condition else y) for simple conditional assignments.