Comparison and logical operators
You met comparisons and and/or/not in the booleans lesson. Now that you
have if, they matter more, so this lesson goes deeper: precedence, membership,
the difference between is and ==, and a compact form that is worth knowing.
The comparison operators
print(5 == 5) # True — equal
print(5 != 3) # True — not equal
print(5 > 3) # True
print(5 < 3) # False
print(5 >= 5) # True
print(5 <= 4) # False
They work on strings too, comparing alphabetically — more precisely, by character code:
print("apple" < "banana") # True
print("Zebra" < "apple") # True
That second one surprises people. Capital letters have lower character codes
than lowercase ones, so every capitalised word sorts before every lowercase
one. When sorting names, compare .lower() versions unless you genuinely want
that behaviour.
Chaining
Python lets you write a range the way mathematics does:
score = 75
print(0 <= score <= 100) # True
Most languages force score >= 0 && score <= 100. Python's version is shorter
and reads better, and it is not a trick — chaining is a real feature, and the
middle value is only evaluated once.
Precedence
When you mix and, or and not, they apply in this order:
notandor
So this:
if is_admin or is_owner and is_active:
means is_admin or (is_owner and is_active) — which may well not be what the
author meant. An admin gets through regardless of whether they are active.
Write the brackets:
if is_admin or (is_owner and is_active):
Same behaviour, no ambiguity, and the next person does not have to remember a
precedence table. When you mix and with or, always bracket. This is not
a style preference; it is the difference between a security check that works
and one that quietly does not.
Short-circuiting, and why it protects you
and stops as soon as it hits something false. or stops as soon as it hits
something true.
That makes this safe:
if name and name.strip():
print("valid")
If name is None or "", the left side is falsy, the right side never runs,
and None.strip() never gets the chance to raise AttributeError.
Swap the order and it breaks:
if name.strip() and name: # AttributeError when name is None
Put the protective condition first. You will use this constantly once you are handling data you did not create.
Membership with in
in asks whether something appears inside something else:
print("@" in "priya@example.com") # True
print("cat" in "concatenate") # True
print(3 in [1, 2, 3]) # True
print("x" in ["a", "b"]) # False
It replaces a chain of ors neatly:
# instead of this
if day == "Saturday" or day == "Sunday":
# write this
if day in ("Saturday", "Sunday"):
Clearer, and adding a third case is a one-word change. not in does the
opposite:
if extension not in (".jpg", ".png", ".webp"):
print("Unsupported image format.")
is versus ==
These look interchangeable and are not.
== asks: do these have the same value?
is asks: are these the same object in memory?
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True — same contents
print(a is b) # False — two separate lists
Two different lists that happen to hold the same things are equal but not identical. Change one and the other is unaffected.
c = a
print(c is a) # True — the same list, under two names
This is the "label on a value" picture from the variables lesson, made
concrete. c and a are two labels on one list.
The rule in practice: use == for values. Use is only for None, True
and False.
if value is None: # correct
if value == None: # works, but do not
A trap worth seeing once
x = 256
y = 256
print(x is y) # True
x = 257
y = 257
print(x is y) # False
Same code, different answer. Python keeps a cache of small integers (roughly −5 to 256) and reuses them, so two small numbers can genuinely be the same object while two larger ones are not.
The lesson is not to memorise the range. It is: is on numbers gives answers
that depend on implementation details, so never use it there. If you find
yourself writing if count is 0, change it to ==.
The conditional expression
When an if/else exists only to pick between two values, Python has a compact
form:
age = 20
status = "adult" if age >= 18 else "minor"
print(status)
adult
Read it as "this, if the condition, otherwise that". The equivalent long form:
if age >= 18:
status = "adult"
else:
status = "minor"
Both are fine. The short form earns its place inside an f-string, where the long form cannot go:
count = 1
print(f"{count} item{'s' if count != 1 else ''}")
1 item
Use it for simple either/or choices. The moment you find yourself nesting one
inside another, go back to a normal if — nested conditional expressions are
genuinely hard to read and nobody thanks you for them.
Check your work
The comparisons.
| Expression | Result | Why |
|---|---|---|
"Apple" < "apple" |
True |
capitals have lower character codes |
"10" < "9" |
True |
string comparison, character by character |
10 < 9 |
False |
numeric comparison |
The middle one catches anyone sorting version numbers or quantities that arrived as text.
Between 1 and 100.
print(1 <= number <= 100)
The precedence question. With is_admin = False, is_owner = True,
is_active = False:
is_admin or is_owner and is_active # False
(is_admin or is_owner) and is_active # False
is_admin or (is_owner and is_active) # False — the default grouping
Change is_active to True and the first two give True and True; set
is_admin = True with is_active = False and you get True against False.
That difference is a security check passing when it should not. Bracket
whenever and and or appear together.
Rewriting with in.
if day in ("Mon", "Tue", "Wed"):
Adding a fourth day is one word.
== versus is.
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b, a is b) # True False
c = a
print(c is a) # True
Two lists with the same contents are equal and not identical. c = a sticks a
second label on one list.
256 and 257. x is y is True for 256 and False for 257, because Python
caches small integers and reuses them. The lesson is not the range — it is that
is on numbers depends on implementation details, so never use it there.
Use is only for None, True and False.
The conditional expression.
advice = "Stay hydrated" if temperature > 30 else "Carry a jacket"
Practice
-
Predict then check:
"Apple" < "apple","10" < "9",10 < 9. The middle one is string comparison and catches people sorting version numbers. -
Write a check for whether a number is between 1 and 100 inclusive, using chaining.
-
Given
is_admin = False,is_owner = True,is_active = False, evaluateis_admin or is_owner and is_activeby hand, then run it. Then bracket it the other way and see the answer change. -
Rewrite
if day == "Mon" or day == "Tue" or day == "Wed"usingin. -
Create two lists with identical contents. Show that
==isTrueandisisFalse. Then assign one to a new name and showisbecomesTrue. -
Run the 256 / 257 experiment. Then write down, in one sentence, when you should use
is. -
Rewrite this as a conditional expression:
if temperature > 30: advice = "Stay hydrated" else: advice = "Carry a jacket"
Next: for loops, and doing something to every item without writing it out.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship