PythonComparison Operators

Comparison Operators

Comparison operators ask a question about two values and return a boolean answer: True or False. They're the building blocks of every if statement, while loop condition, and filter you'll write. Python supports the six comparisons you'd expect from any language, but it also adds a genuinely distinctive feature — chained comparisons — and enforces stricter rules than many languages about which types can be compared at all.

The six comparison operators

Operator

Meaning

Example

Result

==

Equal to

3 == 3

True

!=

Not equal to

3 != 4

True

>

Greater than

5 > 3

True

<

Less than

5 < 3

False

>=

Greater than or equal to

5 >= 5

True

<=

Less than or equal to

4 <= 3

False

`==` vs `is`
`==` compares *values* for equality. It is not the same as `is`, which checks *identity* — whether two names refer to the exact same object in memory. `[1, 2] == [1, 2]` is `True`, but `[1, 2] is [1, 2]` is `False` because they're two different list objects with equal contents. The identity and membership operators page covers `is` in depth.
Chained comparisons — a distinctly Pythonic feature

Most languages don't let you write 1 < x < 10 and have it mean what you'd hope. In C, Java, or JavaScript, that expression is evaluated left to right: (1 < x) < 10 first computes a boolean (True or False, i.e. 1 or 0), and then compares that to 10 — which is almost always True, regardless of what x actually is.

Python special-cases this pattern. A chained comparison like a < b < c is evaluated as a < b and b < c, with b evaluated only once, short-circuiting if the first part is false. This reads naturally and matches how you'd write the inequality in math class.

Chained comparison vs. the naive translation

Python
x = 15

# Pythonic chained comparison — this is what you actually want:
print(1 < x < 10)   # False, because x (15) is not less than 10

# What it's really doing under the hood:
print(1 < x and x < 10)   # False -- same result

# Compare that to what a C-style language would do if < just
# chained left-to-right on booleans. We can simulate that here:
result_of_first = (1 < x)      # True
naive = result_of_first < 10   # True < 10 -> True (bool is a subclass of int)
print(naive)   # True -- very different answer!
False
False
True

This isn't just a toy example — it proves the two interpretations genuinely disagree for x = 15. Python's chained form correctly reports False (15 is not between 1 and 10), while naively re-applying < to a boolean result gives the misleading True. You can chain as many comparisons as you like, e.g. 0 <= score <= 100, and even mix operators, e.g. a < b <= c != d.

Comparing different types

Python 2 allowed comparing almost any two objects with < or > (using an arbitrary but consistent ordering), which quietly hid bugs. Python 3 tightened this up: comparing genuinely incompatible types with ordering operators raises a TypeError. Equality checks (==, !=) are more forgiving — comparing unrelated types with == simply returns False rather than raising.

Ordering incompatible types raises TypeError

Python
print(5 == "5")     # False -- no error, just not equal
print(5 != "5")     # True

print(5 < "5")       # raises TypeError
# Traceback (most recent call last):
#   ...
# TypeError: '<' not supported between instances of 'int' and 'str'
False
True
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'
Numbers are the exception
Python's numeric tower (`int`, `float`, `complex`, `bool`) can be compared and ordered with each other freely — `3 < 3.5` and `True == 1` both work fine, because these types are considered compatible for comparison purposes even though they're different classes.
Comparing strings lexicographically

Strings compare character by character using Unicode code point values, the same way you'd alphabetize words in a dictionary — this is called lexicographic ordering. Comparison stops at the first differing character; if one string is a prefix of the other, the shorter string is considered smaller.

Python
print("apple" < "banana")   # True  -- 'a' < 'b'
print("apple" < "apply")    # True  -- differ at 5th char, 'e' < 'y'
print("Zebra" < "apple")    # True  -- 'Z' (90) has a lower code point than 'a' (97)
print("app" < "apple")      # True  -- "app" is a prefix, so it's "smaller"
Case matters
Because comparison uses raw Unicode code points, all uppercase letters sort before all lowercase letters. If you want case-insensitive comparisons, normalize both sides first, e.g. `a.lower() < b.lower()`.
Comparing custom objects

By default, comparing two instances of a class you wrote with == checks identity (same as is), because Python doesn't know what "equal" means for your data unless you tell it. You can customize this behavior by defining the __eq__ method (and __lt__, __gt__, and friends for ordering).

Coming up later
Teaching a class how to respond to `==`, `&lt;`, and the rest of the comparison operators is covered in depth on the dunder / magic methods page — that's where `__eq__`, `__lt__`, and the `functools.total_ordering` shortcut live.