Python Truthy and Falsy Values, is vs == and None Explained
Empty and zero values are falsy in Python, everything else truthy. What and/or return, why x is None beats == None, is versus == and chained comparisons.
- Course: Python study plan
- Module: Values, types and operators
- Kind: Lesson
- Reading time: 13 min
- Runtime: CPython 3.11
What are truthy and falsy values in Python?
In Python, every object is truthy or falsy when tested by if, while, and, or and not. The falsy values are False, None, numeric zero (0, 0.0, 0j), the empty string and empty containers such as [], (), {}, set() and range(0); everything else is truthy, including "False", "0", [0] and -1.
Lesson
Every Python object is either truthy or falsy, and the language's conditionals, and, or, not, any and all are defined over that property rather than over bool alone. That one rule makes if xs: the idiom for "if the list is non-empty" and x or default the idiom for a fallback — and makes if count: a bug when zero is a legitimate count. This lesson fixes the truthiness table, explains what and and or actually return, settles None and the difference between identity (is) and equality (==), and shows how comparison chaining reads.
bool is an int
True and False are the two instances of bool, and bool is a subclass of int with values 1 and 0. Consequences that are used on purpose: True + True == 2, sum(x > 0 for x in xs) counts the positives, xs[flag] picks the second element when flag is true. Consequences to avoid: True == 1 is True, {1: "a", True: "b"} has one key, and isinstance(True, int) is True — code that checks type(x) is int treats booleans as integers.
Comparison operators return bool: 3 < 5 is True, "a" == "a" is True. bool(x) converts anything using the truthiness rules below.
The truthiness table
| Falsy | Everything else is truthy |
|---|---|
False, None | True |
0, 0.0, 0j, Decimal("0"), Fraction(0) | any non-zero number, including -1 and 0.001 |
"" (empty string) | any non-empty string — including "False", "0" and " " |
[], (), {}, set(), range(0) | any non-empty container — including [0], [None] and [[]] |
objects whose __bool__ returns False or whose __len__ returns 0 | objects with neither method |
"Empty and zero are false; everything else is true" covers it. A user-defined class is truthy unless it defines __bool__ or __len__ (Module 8). The classic bug is the string "False", which is true because it is non-empty; the second classic is if n: where n may legitimately be 0.
def describe(xs):
if xs: # non-empty
return f"{len(xs)} items"
return "empty"
def find(xs, target):
idx = xs.index(target) if target in xs else None
if idx is not None: # NOT `if idx:` — index 0 is a real answer
return idx
and, or, not — what they return
not x returns a bool. and and or do not: they return one of their operands, and they short-circuit.
x and yevaluatesx; if it is falsy the result isx(andyis never evaluated); otherwise the result isy.x or yevaluatesx; if it is truthy the result isx; otherwise the result isy.
print(0 or "default") # 'default'
print("name" or "default") # 'name'
print("" and 5) # '' — the falsy left operand
print(3 and 0) # 0 — the right operand, whatever it is
print(3 and 7) # 7
The short-circuit is the guard idiom: xs and xs[0] never indexes an empty list; d.get("k") or fallback supplies a default when the key is missing or its value is falsy. That last clause is the trap: count or 10 replaces a genuine 0 with 10; when zero is meaningful, write count if count is not None else 10. In a condition the returned operand is truth-tested anyway, so the distinction only shows when the result is stored or printed.
any(iterable) is True if any element is truthy; all(iterable) if every one is (and all([]) is True — vacuous truth). Both short-circuit and take a generator, so any(x < 0 for x in xs) stops at the first negative.
None
None is the single instance of NoneType: the value of a function that does not return, the default for an optional argument, the "no result" of dict.get and re.match. It is falsy, but the test for it is identity, x is None / x is not None, never x == None — a class could define __eq__ to say it equals None, and is asks the only question you mean: "is this the None object?".
is versus ==
== asks whether two objects have equal values (each class decides, through __eq__). is asks whether two names refer to the same object — the same identity, the same id(). Two equal lists are not the same list:
a = [1, 2]
b = [1, 2]
c = a
print(a == b, a is b, a is c) # True False True
Use is only for singletons: None, True, False, NotImplemented, Ellipsis, and sentinel objects you created yourself. Never for numbers or strings: CPython caches small integers (−5 to 256) and interns some strings, so x is 5 may be True today and False for x = 1000 — the interpreter warns SyntaxWarning: "is" with a literal for this exact mistake. x == 5 is always the right question.
Comparison chaining
a < b < c means a < b and b < c, with b evaluated once. Any comparison operators chain: 0 <= i < len(xs) is the bounds check, a == b == c tests three-way equality, x < y > z is legal but unreadable. This reads as mathematics, unlike C, where a < b < c compares a boolean with c. in and is are comparison operators too: x in xs, x not in xs, a is not None all chain by the same rule.
Conditional expressions
value_if_true if condition else value_if_false is Python's ternary; the condition sits in the middle, which reads naturally once seen a few times: label = "even" if n % 2 == 0 else "odd". It is an expression, so it goes inside f-strings and arguments. Nesting more than one level is legal and discouraged.
Pitfalls
if count:when0is a valid count;if s:when"0"should be false.x or defaultreplacing a legitimate falsy value —0,"",[].x == None. Useis None.x is 5,s is "abc". Identity on values is an accident of caching.- Believing
and/orreturn booleans;print(a and b)prints an operand. all([])isTrue— check emptiness separately when it matters.
Key takeaways
boolis anintsubclass; comparisons return it;sumof booleans counts.- Empty and zero are falsy, everything else truthy — including
"False",[0]and-1. and/orreturn an operand and short-circuit;not,any,allreturn booleans.isis identity, forNoneand sentinels only;==is value equality.- Comparisons chain:
0 <= i < n; the conditional expression isa if c else b.
Common questions
What is the difference between is and == in Python?
== compares values, as each class defines through __eq__; is checks whether two names refer to the very same object. Two equal lists are == but not is. Use is only for singletons such as None, True, False and your own sentinels — never for numbers or strings.
Why use is None instead of == None in Python?
None is a single object, so x is None asks exactly the question you mean: is this the None object? x == None calls the class's __eq__, which could be defined to claim equality with None. Write is None and is not None.
What do and and or return in Python?
They return one of their operands, not necessarily a bool, and they short-circuit. x or y returns x if it is truthy, otherwise y, so 0 or "default" is "default"; x and y returns x if it is falsy, otherwise y, so 3 and 0 is 0. Only not always returns a bool.
Does Python have a ternary operator?
Yes, the conditional expression value_if_true if condition else value_if_false, for example label = "even" if n % 2 == 0 else "odd". It is an expression, so it works inside f-strings and function arguments; nesting it more than one level deep is legal but hard to read.
How do chained comparisons work in Python?
a < b < c means a < b and b < c, with b evaluated only once. Any comparison operators chain, so 0 <= i < len(xs) is the idiomatic bounds check and a == b == c tests three-way equality — unlike C, where a < b < c compares a boolean with c.
Exercises
Truthy or falsy
Read n Python literals, one per line — numbers, strings in quotes, lists, tuples, dicts, None, True/False — turn each into a value with ast.literal_eval (which parses literals without running code) and report its truth value.
Input: n, then n literals. Output: <literal as written> is truthy or ... is falsy.
4
0
""
[0]
"False"
prints
0 is falsy
"" is falsy
[0] is truthy
"False" is truthyChained comparison with missing values
Each line holds three values a b c, each an integer or - meaning missing. Decide whether a < b < c holds using a chained comparison; when any value is missing the answer is unknown. Represent a missing value as None and test for it with is None.
Input: n, then n lines. Output: True, False or unknown per line.
4
1 2 3
3 2 1
1 - 3
2 2 3
prints
True
False
unknown
FalseIn this module: Values, types and operators
- Numbers — int, float and the arithmetic that surprises
- Floating point — why 0.1 + 0.2 is not 0.3, and what to do about it
- Booleans, truthiness, None, and is versus == (this lesson)
- Names, binding and mutability — there are no boxes
- Conversions — between text, numbers and containers
- Operators and precedence
- Checkpoint — Values, types and operators
← Floating point — why 0.1 + 0.2 is not 0.3, and what to do about it · Names, binding and mutability — there are no boxes →