A boolean in Python is a value that is either True or False, and nothing else:
is_ready = True
is_done = False
type(is_ready) # <class 'bool'>
isinstance(is_ready, bool) # True
Both must be capitalised. Lowercase true is just an undefined name, so it raises a NameError.
The trap worth knowing before anything else: bool("False") is True, because any non-empty string is truthy.
Everything on this page was run on Python 3.12.5, NumPy 2.5.3.
What is a boolean in Python?
It is the type with exactly two values, and it is what every comparison returns:
is_ready = True
is_done = False
print(is_ready, type(is_ready))
print(is_done, type(is_done))
print()
# comparisons produce booleans
print("3 > 2 ->", 3 > 2)
print("'a' in 'cat' ->", "a" in "cat")
print("5 == 5.0 ->", 5 == 5.0)
print()
# they must be capitalised
try:
value = true
except NameError as err:
print("true (lowercase) ->", type(err).__name__ + ":", err)
Output:
True <class 'bool'>
False <class 'bool'>
3 > 2 -> True
'a' in 'cat' -> True
5 == 5.0 -> True
true (lowercase) -> NameError: name 'true' is not defined
3 > 2 does not return the number 1 or the string “yes”. It returns the boolean True, which is why you can assign a comparison straight to a variable.
Python is case sensitive here. True and False are keywords; true and false are not defined at all.
Truthy and falsy values in Python
Every Python object can be used in an if, not just booleans. bool() shows you which way each one goes:
# every object is either truthy or falsy
falsy = [0, 0.0, "", [], {}, (), set(), None, False]
truthy = ["0", "False", [0], 0.1, " ", [[]]]
print("FALSY:")
for value in falsy:
print(f" bool({value!r:<8}) = {bool(value)}")
print("\nTRUTHY (these surprise people):")
for value in truthy:
print(f" bool({value!r:<8}) = {bool(value)}")
Output:
FALSY:
bool(0 ) = False
bool(0.0 ) = False
bool('' ) = False
bool([] ) = False
bool({} ) = False
bool(() ) = False
bool(set() ) = False
bool(None ) = False
bool(False ) = False
TRUTHY (these surprise people):
bool('0' ) = True
bool('False' ) = True
bool([0] ) = True
bool(0.1 ) = True
bool(' ' ) = True
bool([[]] ) = True

"0", is truthy.- Falsy —
0,0.0,"",[],{},(),set(),None,False. - Truthy — everything else, including
"0","False",[0]and a single space. - A list containing only falsy items is still truthy, because the list itself is not empty.
This is why if my_list: is the idiomatic way to check for a non-empty list. It reads better than len(my_list) > 0 and does the same job. NumPy arrays are the exception, as checking an empty NumPy array explains.
Why is bool(“False”) True in Python?
This catches out anyone reading booleans from a config file, a form or an environment variable:
answer = "False"
print('bool("False") ->', bool(answer), " <- the string is not empty, so it is True")
print('bool("0") ->', bool("0"), " <- same reason")
print('bool("") ->', bool(""))
print()
# so this check is always wrong
if answer:
print("naive check: treated as True")
print()
# convert properly instead
def to_bool(text):
return text.strip().lower() in {"true", "1", "yes", "y", "on"}
for text in ["True", "false", "1", "0", "yes", "no"]:
print(f" to_bool({text!r:<8}) = {to_bool(text)}")
Output:
bool("False") -> True <- the string is not empty, so it is True
bool("0") -> True <- same reason
bool("") -> False
naive check: treated as True
to_bool('True' ) = True
to_bool('false' ) = False
to_bool('1' ) = True
to_bool('0' ) = False
to_bool('yes' ) = True
to_bool('no' ) = False

"False" and "0" are both non-empty strings, so both are truthy.bool() on a string asks one question only: is it empty? The contents are never inspected.
Parse the text yourself, as the helper above does. There is a fuller treatment in converting a string to a boolean.
How to check if a variable is a boolean
isinstance is the direct answer, and it behaves as you would expect in this direction:
import numpy as np
values = [True, 1, 0, "True", np.bool_(True)]
print(f"{'value':<8} {'isinstance(x, bool)':>20} {'type(x) is bool':>18}")
for value in values:
print(f"{str(value):<8} {isinstance(value, bool)!s:>20} {type(value) is bool!s:>18}")
print()
print("numpy has its own boolean type:", type(np.bool_(True)))
print("so isinstance(np.bool_(True), bool) is", isinstance(np.bool_(True), bool))
Output:
value isinstance(x, bool) type(x) is bool
True True True
1 False False
0 False False
True False False
True False False
numpy has its own boolean type: <class 'numpy.bool'>
so isinstance(np.bool_(True), bool) is False

1 is not a boolean, and neither is NumPy’s bool_.| Check | Accepts | Note |
|---|---|---|
isinstance(x, bool) | True, False | The usual answer |
type(x) is bool | True, False | Same here, since bool cannot be subclassed |
x in (True, False) | Also 1 and 0 | Because True == 1 |
Avoid the third row. 1 in (True, False) is True, because equality rather than identity is used.
NumPy’s bool_ is a separate type and fails isinstance(x, bool). That mirrors the integer case in checking for an integer.
Why is a Python boolean also an integer?
This explains several behaviours that otherwise look like bugs:
# bool is a subclass of int, which explains a lot of odd behaviour
print("bool inherits from:", [cls.__name__ for cls in bool.__mro__])
print()
print("int(True) :", int(True))
print("int(False) :", int(False))
print("True + True:", True + True)
print("sum([True, False, True]):", sum([True, False, True]), " counting matches")
print()
print("isinstance(True, int) :", isinstance(True, int), " <- True is also an integer")
print("True == 1 :", True == 1)
print("True is 1 :", True is 1, " but not the same object")
Output:
C:\pyguides\runs\boolean\ex_subclass.py:13: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?
print("True is 1 :", True is 1, " but not the same object")
bool inherits from: ['bool', 'int', 'object']
int(True) : 1
int(False) : 0
True + True: 2
sum([True, False, True]): 2 counting matches
isinstance(True, int) : True <- True is also an integer
True == 1 : True
True is 1 : False but not the same object
True is 1 and False is 0, all the way down. That is why True + True is 2 and why sum() counts a list of booleans.
It is also why an integer check accepts a boolean. If that matters, add and not isinstance(x, bool) to the test.
Note the last line: True == 1 is True but True is 1 is False. They compare equal without being the same object.
Boolean operators: and, or and not
The three operators do more than combine booleans. They short-circuit, and they return operands:
print("and / or / not:")
print(" True and False ->", True and False)
print(" True or False ->", True or False)
print(" not True ->", not True)
print()
# they short-circuit: the right side is skipped when the answer is already known
def noisy():
print(" (the right side ran)")
return True
print(" False and noisy() ->", False and noisy())
print(" True or noisy() ->", True or noisy())
print()
# and they return an OPERAND, not a boolean
print(" 'a' and 'b' ->", repr("a" and "b"))
print(" 0 or 'fallback' ->", repr(0 or "fallback"))
print(" [] or None ->", repr([] or None))
Output:
and / or / not:
True and False -> False
True or False -> True
not True -> False
False and noisy() -> False
True or noisy() -> True
'a' and 'b' -> 'b'
0 or 'fallback' -> 'fallback'
[] or None -> None

'a' and 'b' returns 'b', not True.Short-circuiting means the right-hand side is skipped once the answer is decided. False and anything() never calls the function.
That is what makes if user and user.is_active: safe. If user is None, the attribute access never happens.
or returning an operand gives you the default-value idiom: name = supplied or "Anonymous". Be careful with it when 0 or an empty string is a legitimate value.
Counting with booleans
Because True is 1, booleans and sum() combine into a neat counting idiom:
scores = [45, 82, 91, 38, 67, 95]
passed = [s >= 50 for s in scores]
print("scores :", scores)
print("passed :", passed)
print()
# because True is 1, sum() counts them
print("how many passed:", sum(passed))
print("did any pass :", any(passed))
print("did all pass :", all(passed))
print()
print("percentage:", f"{sum(passed) / len(passed):.0%}")
Output:
scores : [45, 82, 91, 38, 67, 95]
passed : [False, True, True, False, True, True]
how many passed: 4
did any pass : True
did all pass : False
percentage: 67%
sum(passed) counts how many comparisons were true. No loop and no counter variable.
any() and all() answer the other two questions, and both short-circuit as soon as the result is certain.
Common boolean mistakes
| Symptom | Cause | Fix |
|---|---|---|
NameError: true | Lowercase keyword | Use True |
"False" treated as true | Any non-empty string is truthy | Parse the text |
Integer check accepts True | bool subclasses int | Add not isinstance(x, bool) |
1 in (True, False) is true | Equality, not identity | Use isinstance(x, bool) |
| NumPy value fails a bool check | np.bool_ is a different type | Call bool(x) first |
or default overrides 0 | 0 is falsy | Compare with is None |
Related Python type and conversion guides:
- Convert a string to a boolean
- Check if a variable is an integer
- Check if a variable is a string
- Check if a string is a boolean value
- Python variables explained
- Check if a variable is null or empty
Frequently asked questions
What is a boolean in Python?
A value that is either True or False. It is the type every comparison returns, and it is documented in the Python boolean type reference.
How do I check if a variable is a boolean in Python?
isinstance(value, bool). Avoid value in (True, False), which also accepts 1 and 0.
Why is bool(“False”) True in Python?
bool() on a string only checks whether it is empty. "False" has five characters, so it is truthy.
What values are falsy in Python?
0, 0.0, "", [], {}, (), set(), None and False. Everything else is truthy.
Is a boolean an integer in Python?
Yes. bool is a subclass of int, so True is 1 and True + True is 2.
Why does ‘a’ and ‘b’ return ‘b’ instead of True?
and and or return one of their operands rather than a boolean. and returns the last value when everything is truthy.
Why does true give a NameError?
Python’s keywords are capitalised. Write True and False.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn