10.1 The bool Type and its Two Values
The bool type in Python represents one of two possible values: True or False. These are the only two instances of the bool class and are singletons, meaning there is only one copy of each in a Python program. This type is the result of comparison operations (==, !=, <, >, etc.) and logical operations (and, or, not), and it is fundamental to controlling the flow of programs through conditional statements (if, elif, while).
Underneath its simple exterior, the bool type is a direct subclass of the int type. This inheritance is a legacy from Python’s past and its influence from other languages like C. This relationship means that True has an integer value of 1 and False has an integer value of 0.
# Demonstrating that bool is a subclass of int
print(issubclass(bool, int)) # Output: True
# Demonstrating the integer values of True and False
print(int(True)) # Output: 1
print(int(False)) # Output: 0
# This allows for mathematical operations (though not always recommended)
print(True + True + False) # Output: 2
print(True * 10) # Output: 10
The Nature of True and False
It is crucial to understand that True and False are keywords in Python and must be capitalized. Using lowercase (true, false) will result in a NameError. Because they are singletons, you can and should use the is operator for identity checks when comparing to these literal values. The is operator checks if two variables point to the exact same object in memory, which is always the case for True and False.
# Correct usage
result = 5 > 3
if result is True: # Identity check
print("It's true!")
# Incorrect usage that will cause an error
try:
if result is true:
print("This won't work.")
except NameError as e:
print(f"Error: {e}")
However, due to the concept of “truthiness” (covered in detail in the next section), directly comparing to True or False using == or is is often unnecessary and can be considered un-Pythonic. The preferred style is to simply use the value in a conditional context.
# Less Pythonic
if (5 > 3) is True:
print("This works, but is verbose.")
# Pythonic
if 5 > 3:
print("This is the preferred way.")
Boolean Operations: and, or, not
The logical operators and, or, and not are used to combine or modify boolean values. It is vital to understand that these operators do not simply return True or False; they return one of the objects being evaluated. This behavior is called short-circuit evaluation.
and: Evaluates expressions from left to right and returns the first falsy value. If all values are truthy, it returns the last value.or: Evaluates expressions from left to right and returns the first truthy value. If all values are falsy, it returns the last value.not: ReturnsTrueif the operand is falsy andFalseif the operand is truthy. This operator always returns a strictboolvalue.
# 'and' returns the first falsy value or the last truthy value.
print(0 and 42) # Output: 0 (falsy)
print([] and "Hello") # Output: [] (falsy)
print(10 and 20) # Output: 20 (last truthy value)
# 'or' returns the first truthy value or the last falsy value.
print(0 or 42) # Output: 42 (truthy)
print([] or "Hello") # Output: "Hello" (truthy)
print(0 or [] or "") # Output: "" (last falsy value)
# 'not' always returns a boolean.
print(not True) # Output: False
print(not []) # Output: True
print(not "Hello") # Output: False
Common Pitfalls and Best Practices
A common pitfall arises from the integer inheritance of booleans. Because True == 1 and False == 0 evaluate to True, they can be used interchangeably in numeric contexts, which can lead to confusing and subtle bugs.
# A potentially confusing situation
my_list = ['a', 'b', 'c']
index = False # which is 0
print(my_list[index]) # Output: 'a'
index = True # which is 1
print(my_list[index]) # Output: 'b'
Best Practice: Avoid using True and False in arithmetic operations or as list indices. Use the integer literals 1 and 0 if that is your intent, as it makes the code vastly more readable and its purpose explicit.
Another best practice is to leverage the return value of and and or for concise and elegant code patterns. A classic example is providing a default value.
# Setting a default value if a variable is empty
user_input = ""
name = user_input or "Guest"
print(name) # Output: "Guest"
# Conditional assignment based on a check
value = 10
result = (value > 5 and "Large") or "Small"
print(result) # Output: "Large"
However, one must be cautious with the and/or pattern for conditional assignment, as it can fail if the first term in an and could be a falsy value that you want to keep. In modern Python, the ternary conditional operator is often clearer.
# Safer and clearer with the ternary operator
value = 0
# Potentially problematic with 'and'/'or'
temp = value and "Has value" or "No value" # Output: "No value" (incorrect if 0 is a valid value)
# Clear and correct with ternary
temp = "Has value" if value else "No value"
print(temp)