RizTech Academy logo
RizTech Academy
Control FlowLesson 2 of 615 min

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:

  1. not
  2. and
  3. or

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

  1. Predict then check: "Apple" < "apple", "10" < "9", 10 < 9. The middle one is string comparison and catches people sorting version numbers.

  2. Write a check for whether a number is between 1 and 100 inclusive, using chaining.

  3. Given is_admin = False, is_owner = True, is_active = False, evaluate is_admin or is_owner and is_active by hand, then run it. Then bracket it the other way and see the answer change.

  4. Rewrite if day == "Mon" or day == "Tue" or day == "Wed" using in.

  5. Create two lists with identical contents. Show that == is True and is is False. Then assign one to a new name and show is becomes True.

  6. Run the 256 / 257 experiment. Then write down, in one sentence, when you should use is.

  7. 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