A handful of values are falsy (treated as False in a boolean context):
None, False, zero of any numeric type (0, 0.0, 0j), and
empty containers/sequences ("", [], {}, (), set(), range(0)).
Almost everything else is truthy.
bool(0), bool(""), bool([]), bool(None) # all False
bool(1), bool("x"), bool([0]), bool(" ") # all True
if not items: # idiomatic empty check
print("empty")
Why it matters: idiomatic Python uses if items: rather than if len(items) > 0:.
Rule of thumb: "empty or zero or None" is falsy; everything else is truthy.
Python calls __bool__ first; if it's not defined, it falls back to
__len__ (zero length is falsy). If neither exists, the object is always
truthy — the default for plain instances.
class Box:
def __init__(self, items): self.items = items
def __len__(self): return len(self.items) # used for truthiness
bool(Box([])) # False — __len__ is 0
bool(Box([1])) # True
class Always:
def __bool__(self): return False # __bool__ wins over __len__
bool(Always()) # False
Rule of thumb: define __bool__ (or __len__) so if obj: makes sense for your
type; otherwise every instance is truthy.
Python provides constructor functions that convert between types: int(),
float(), str(), bool(), list(), tuple(), set(), dict(). They build a
new object and raise ValueError if the input can't be converted.
int("42") # 42
int("3.9") # ValueError — not a valid int literal
int(3.9) # 3 — truncates toward zero
float("3.14") # 3.14
str(255) # "255"
list("abc") # ['a', 'b', 'c']
list({1: "a"}) # [1] — iterates keys
Rule of thumb: these are explicit conversions you call yourself — Python rarely converts types implicitly, so reach for the constructor you need.
Yes. Every empty built-in container is falsy — [], {}, (), set(),
"" — and so is None. A non-empty container is truthy regardless of what it
contains, even [0] or [False].
bool([]), bool({}), bool(set()), bool("") # all False
bool([0]), bool([False]), bool({None}) # all True — non-empty!
bool(None) # False
Watch the trap: [0] is truthy because it has one element, even though that
element is falsy. Rule of thumb: container truthiness depends on length, not on
the elements' values.
and/or short-circuit and return one of their operands, not a strict
True/False. and returns the first falsy operand (or the last if all are
truthy); or returns the first truthy operand (or the last if all are falsy).
0 and 5 # 0 — first falsy, second never evaluated
2 and 5 # 5 — both truthy, returns last
0 or "default" # "default" — first truthy
None or 0 or [] # [] — all falsy, returns the last
name = user_input or "guest" # common default idiom
Rule of thumb: x or default supplies a fallback, and short-circuiting means the
right side is skipped when the result is already decided.
None is a singleton — there is exactly one None object — so is None
checks identity, which is fast and can't be fooled. == None calls __eq__,
which a class can override to return a misleading result.
if x is None: # idiomatic, reliable
...
class Weird:
def __eq__(self, other): return True
Weird() == None # True — misleading!
Weird() is None # False — correct
Rule of thumb: always compare against None, True, and False with is/
is not — PEP 8 explicitly recommends it.
if x: is falsy for many valid values — 0, "", [], 0.0 — not just
None. If your real question is "was an argument supplied?", a bare truthiness check
silently rejects legitimate empty/zero inputs.
def f(count=None):
if not count: # BUG: triggers for count=0 too
count = 10
return count
f(0) # 10 — wanted 0!
def g(count=None):
if count is None: # correct: only the unset case
count = 10
return count
g(0) # 0
Rule of thumb: distinguish "missing" from "empty/zero" — use is None for sentinels,
reserve if x: for genuine emptiness checks.
bool(n) is False only for zero; any non-zero number is True. bool(s) is
False only for the empty string — so bool("0") and bool("False") are both
True (non-empty strings are truthy regardless of content).
bool(0), bool(0.0), bool(-1) # (False, False, True)
bool(""), bool("0"), bool("False") # (False, True, True) — trap!
int(True), int(False) # (1, 0)
Rule of thumb: parsing input like "0"/"false" needs explicit checks — never trust
bool(string) to interpret the content, only its emptiness.
any(iterable) returns True if at least one element is truthy; all returns
True if every element is truthy. Both short-circuit and have edge cases on
empty input: all([]) is True (vacuous truth), any([]) is False.
any([0, "", 3]) # True — 3 is truthy
all([1, 2, ""]) # False — "" is falsy
all([]) # True — nothing fails
any([]) # False — nothing succeeds
all(x > 0 for x in nums) # combine with a generator
Rule of thumb: all/any over a generator expression is the idiomatic "do all/any
items satisfy this?" — but remember all([]) is True.
Python implicitly widens numbers in mixed arithmetic: int → float →
complex, picking the more general type. It does not implicitly convert between
strings and numbers — that always raises TypeError.
1 + 2.0 # 3.0 — int promoted to float
3 + 4j # (3+4j) — promoted to complex
True + 1.5 # 2.5 — bool is an int
"3" + 4 # TypeError — no str/int coercion
Rule of thumb: numeric types auto-promote to the wider type; everything else needs an
explicit constructor (int(s), str(n)).
It compares values via chaining: a == b == c means a == b and b == c, with
b evaluated once. It is not (a == b) == c, which would compare a bool to c.
1 == 1 == 1 # True — (1==1) and (1==1)
(1 == 1) == 1 # True — but for the wrong reason: True == 1
(1 == 1) == 2 # False — True == 2
True == 1 == 1.0 # True — bool/int/float all equal here
Rule of thumb: chained == checks all neighbors are equal; never parenthesize it as
(a == b) == c, which collapses a comparison into a bool.
int(s, base) parses a string in any base 2-36; bin(), oct(), hex() produce
prefixed string representations. The reverse and forward directions are separate
tools.
int("ff", 16) # 255
int("1010", 2) # 10
int("0o17", 0) # 15 — base 0 auto-detects from prefix
bin(10) # '0b1010'
hex(255) # '0xff'
format(255, "x") # 'ff' — no prefix
Rule of thumb: int(s, base) to parse, bin/oct/hex to produce; use format
or f-strings when you want the digits without the 0b/0x prefix.
Converting to set or dict drops duplicates and order info, and converting a
dict to a list/tuple yields its keys, not items. These lossy conversions catch
people off guard.
list({3, 1, 2}) # order not guaranteed by value
set([1, 1, 2]) # {1, 2} — dups removed
list({"a": 1, "b": 2}) # ['a', 'b'] — keys only
dict([("a", 1), ("b", 2)]) # {'a': 1, 'b': 2} — from pairs
tuple("ab") # ('a', 'b')
Rule of thumb: list(dict) gives keys (use .items() for pairs), and set() is a
quick dedupe but discards order and duplicate counts.
str() produces a readable form for end users (via __str__); repr() produces
an unambiguous form for developers (via __repr__), ideally one you could paste
back. If __str__ is missing, str() falls back to __repr__.
import datetime
d = datetime.date(2026, 6, 19)
str(d) # '2026-06-19' — friendly
repr(d) # 'datetime.date(2026, 6, 19)' — reconstructable
str("hi") # 'hi'
repr("hi") # "'hi'" — shows the quotes
Rule of thumb: implement __repr__ for every class (debugging/logging); add
__str__ only when users need a prettier form.
ord(c) returns the integer Unicode code point of a single character;
chr(n) returns the character for a code point. They are inverses and work
across the full Unicode range.
ord("A") # 65
chr(65) # "A"
ord("€") # 8364
chr(8364) # "€"
[chr(ord("a") + i) for i in range(3)] # ['a', 'b', 'c']
Rule of thumb: ord/chr bridge characters and integers — useful for ciphers,
alphabets, and ranges; they handle Unicode, not just ASCII.
nan is truthy — truthiness for floats depends only on being non-zero, and nan
is not zero. This surprises people who expect "not a number" to behave like a falsy
blank.
bool(float("nan")) # True!
bool(0.0) # False
import math
if math.isnan(x): # the correct way to handle nan
...
Rule of thumb: never use truthiness to detect nan — it's truthy; test explicitly
with math.isnan().
More Fundamentals interview questions
More ways to practice
The self-quiz is live. Join our channel for updates, new content & tech tips.