Every useful program makes decisions by asking questions. Is this number big enough? Did the user type the right password? Is this score a new record? These questions are answered with boolean logic — expressions that come out as either True or False, the two values of Python's bool type.
Comparison operators ask a question about two values and hand back a boolean:
print(5 > 3) # True
print(10 == 10) # True
print(4 != 4) # False
The six operators are == (equal), != (not equal), < (less), > (greater), <=, and >=. Notice that equality testing is ==, with two equals signs — a single = assigns a value and never asks a question. Mixing those up is the first mistake every beginner makes.
flowchart LR
A["age >= 10"] --> C{"and"}
B["height >= 120"] --> C
C --> D["can ride"]
style C fill:#3776ab,color:#fff
Combine questions with and, or, not
Real decisions rarely hinge on one question. Logical operators stitch smaller booleans into bigger ones:
and—Trueonly when both sides areTrueor—Truewhen at least one side isTruenot— flipsTruetoFalseandFalseback toTrue
So age >= 10 and height >= 120 is True only when a rider clears both bars, while has_ticket or is_staff lets in anyone holding either credential. Reach for not when you want to invert a condition you already wrote the easy way around.
x = 7 print(x > 5 and x < 10) print(x == 3 or x == 7) print(not x > 10) print(1 < x < 10)
Chained comparisons
Python lets you write a range check the way you would say it out loud: 1 < x < 10 means "x is between 1 and 10, endpoints excluded." Under the hood that expands to 1 < x and x < 10, so both halves have to hold for the whole thing to be True.
Swap to <= and the endpoints are inclusive: 1 <= x <= 10 keeps 1 and 10 as well. Most languages force you to write the two comparisons and join them yourself; Python's chaining is just a friendlier spelling of the same test.
flowchart TD
Q["a and b"] --> T{"is a falsy?"}
T -->|"yes"| F["result is a (b never runs)"]
T -->|"no"| E["result is b"]
style F fill:#b45309,color:#fff
style E fill:#3776ab,color:#fff
Short-circuit: the right side may never run
and and or do something subtle — they stop as soon as the answer is certain. If the left side of an and is already False, the right side cannot rescue it, so Python skips it entirely. If the left side of an or is already True, the right side is likewise ignored.
That behaviour is more than a speed trick. Because or hands back the first truthy value it sees, user_name or "guest" becomes a tidy default: use the real name when there is one, otherwise fall back to "guest". Just remember that 0 and empty strings are falsy, so score or "no score" also fires when score is a perfectly valid zero.
# 'or' returns its first truthy value, which makes a tidy default user_name = "" display_name = user_name or "guest" print(display_name) # guest - the empty string is falsy score = 0 print(score or "no score") # no score - but 0 is a valid score, so beware! # 'and' and 'or' short-circuit: the right side only runs if it could matter print(0 and "never seen") # 0 - right side skipped because 0 is falsy print(7 or "ignored") # 7 - right side skipped because 7 is truthy
Write can_ride(age, height) that returns True only when age is at least 10 and height is at least 120.
def can_ride(age, height):
# return True if age >= 10 and height >= 120
pass
Use
andto require both conditions to be True.age >= 10 and height >= 120returns the right boolean.
def can_ride(age, height):
return age >= 10 and height >= 120
What does this print? Predict all three lines, then Check.
a = 7 b = 3 print(a > 5 and b > 5) print(a > 5 or b > 5) print(not a == 7)
b > 5is False, soandgives False butorstill gives True.notflips the result:a == 7is True, sonot Trueis False.
This is_big(n) is meant to return True when n is greater than 100. Instead it compares the number to True directly, which quietly fails for most values. Fix it so it returns the right boolean.
def is_big(n):
# meant to be True when n is greater than 100
return n == True
Don't compare to True — compare n to the threshold directly.
return n > 100gives the boolean you actually want.
def is_big(n):
return n > 100
A shop gives a discount when a member's cart is at least 50 or anyone's cart is at least 100. Write can_discount(is_member, cart_total) that returns the right boolean. Mind which condition is wrapped in parentheses.
def can_discount(is_member, cart_total):
# True for members spending 50+, or anyone spending 100+
pass
Group the member condition with parentheses: (is_member and cart_total >= 50).
Then
orit against cart_total >= 100 so big carts always qualify.
def can_discount(is_member, cart_total):
return (is_member and cart_total >= 50) or cart_total >= 100
Recap
- Six comparison operators ask questions; remember
==tests equality while=assigns. - Combine conditions with
and(both must hold),or(either holds), andnot(invert one). - Chain comparisons naturally:
1 <= x <= 10keeps the endpoints. andandorshort-circuit — they stop the moment the answer is decided, which makesvalue or defaulta clean idiom for fallbacks.- Test truthiness with
if x:, never withif x == True:.
Next you will put these conditions to work inside if / else branches that choose what your program does next.