7. Booleans and Conditions
In the previous chapters, you learned how to work with numbers, strings, and basic data operations. Now we're ready to explore how Python makes decisions—a fundamental capability that allows programs to respond differently based on varying conditions. At the heart of decision-making in Python are Boolean values and conditions.
Think about everyday decisions: "If it's raining, I'll bring an umbrella." "If the temperature is above 30°C, I'll turn on the air conditioning." These decisions are based on conditions that are either true or false. Python uses the same principle: it evaluates conditions to determine whether they're true or false, then acts accordingly.
In this chapter, we'll explore Boolean values, learn how to create conditions using comparison operators, understand Python's concept of "truthiness," and discover powerful techniques for building complex logical expressions. By the end of this chapter, you'll have mastered the building blocks needed for the if statements and loops you'll learn in upcoming chapters.
7.1) Boolean Values True and False
Python has a special data type called bool (short for Boolean) that represents truth values. This type has exactly two possible values: True and False. Notice that these values are capitalized—this is important in Python. Writing true or false (lowercase) will cause an error because Python won't recognize them.
# boolean_basics.py
# Creating Boolean variables
is_sunny = True
is_raining = False
print(is_sunny) # Output: True
print(is_raining) # Output: False
print(type(True)) # Output: <class 'bool'>
print(type(False)) # Output: <class 'bool'>Boolean values are fundamental because they represent the result of any yes/no question your program might ask: Is this number greater than 10? Does this string contain the letter 'a'? Is the user logged in? Every condition in Python ultimately evaluates to either True or False.
7.1.1) Booleans in Variables and Expressions
You can store Boolean values in variables just like you store numbers or strings. This is useful when you want to track the state of something in your program:
# boolean_variables.py
# Using Boolean variables to track states
is_logged_in = False
has_permission = True
is_valid_email = True
print("User logged in:", is_logged_in) # Output: User logged in: False
print("Has permission:", has_permission) # Output: Has permission: True
print("Valid email:", is_valid_email) # Output: Valid email: True
# Boolean variables can be reassigned
is_logged_in = True
print("User logged in:", is_logged_in) # Output: User logged in: TrueBoolean variables are often named with prefixes like is_, has_, or can_ to make their purpose clear. This naming convention helps you and others reading your code immediately understand that the variable holds a true/false value.
7.2) Comparison Operators and Basic Conditions
While you can create Boolean values directly with True and False, most Boolean values in your programs will come from comparisons—testing relationships between values. Python provides several comparison operators that compare two values and produce a Boolean result.
7.2.1) The Six Comparison Operators
Python has six primary comparison operators:
| Operator | Meaning | Example | Result |
|---|---|---|---|
== | Equal to | 5 == 5 | True |
!= | Not equal to | 5 != 3 | True |
< | Less than | 3 < 5 | True |
> | Greater than | 5 > 3 | True |
<= | Less than or equal to | 5 <= 5 | True |
>= | Greater than or equal to | 5 >= 3 | True |
Let's see these operators in action:
# comparison_operators.py
# Comparing numbers
x = 10
y = 20
print(x == y) # Output: False
print(x != y) # Output: True
print(x < y) # Output: True
print(x > y) # Output: False
print(x <= y) # Output: True
print(x >= y) # Output: False
# Comparisons with equal values
a = 15
b = 15
print(a == b) # Output: True
print(a <= b) # Output: True
print(a >= b) # Output: True7.2.2) Comparing Numbers and Strings
Comparison operators work with many types of values, not just integers:
# comparing_types.py
# Comparing floating-point numbers
temperature = 23.5
print(temperature > 20.0) # Output: True
print(temperature == 23.5) # Output: True
# Comparing strings (alphabetical/lexicographic order)
name1 = "Alice"
name2 = "Bob"
print(name1 < name2) # Output: True
print(name1 == name2) # Output: False
# Comparing strings is case-sensitive
word1 = "Python"
word2 = "python"
print(word1 == word2) # Output: False
# Comparing string lengths using len()
print(len(name1) == len(name2)) # Output: FalseWhen comparing strings, Python uses lexicographic ordering, which is essentially alphabetical order based on Unicode character values. Uppercase letters come before lowercase letters in this ordering, which is why "Python" and "python" are not equal.
7.2.3) Storing Comparison Results
The result of any comparison is a Boolean value, which means you can store it in a variable:
# storing_comparisons.py
# Storing comparison results for later use
age = 25
is_adult = age >= 18
is_senior = age >= 65
is_teenager = 13 <= age <= 19 # We'll learn about chained comparisons soon
print("Is adult:", is_adult) # Output: Is adult: True
print("Is senior:", is_senior) # Output: Is senior: False
print("Is teenager:", is_teenager) # Output: Is teenager: False
# Using stored comparisons in calculations or other expressions
price = 100
discount_eligible = price > 50
print("Discount eligible:", discount_eligible) # Output: Discount eligible: TrueStoring comparison results in descriptively named variables makes your code more readable. Instead of repeatedly writing age >= 18 throughout your program, you can use the variable is_adult, which clearly communicates what you're checking.
7.3) Booleans from Comparisons, Expressions, and the bool() Function
We've seen that comparisons produce Boolean values. But there are other ways to get Boolean values in Python, including using the bool() function to convert other types to Booleans.
7.3.1) The bool() Function
The bool() function converts any value to its Boolean equivalent. This is useful when you want to explicitly check whether a value would be considered true or false in a Boolean context:
# bool_function.py
# Converting numbers to Booleans
print(bool(1)) # Output: True
print(bool(42)) # Output: True
print(bool(-5)) # Output: True
print(bool(0)) # Output: False
print(bool(0.0)) # Output: False
# Converting strings to Booleans
print(bool("Hello")) # Output: True
print(bool("False")) # Output: True
print(bool("")) # Output: False
# Converting None to Boolean
print(bool(None)) # Output: FalseThe bool() function follows specific rules for conversion, which we'll explore in detail in the next section on truthiness and falsiness. For now, notice that most values convert to True, but certain special values like 0, 0.0, empty strings (""), and None convert to False.
7.4) Truthiness and Falsiness in Conditions
One of Python's most powerful features is its concept of truthiness and falsiness. In Python, every value—not just True and False—has an inherent Boolean interpretation. This means you can use any value where a Boolean is expected, and Python will treat it as either true or false.
7.4.1) Falsy Values: What Counts as False
In Python, the following values are considered falsy (they behave like False in Boolean contexts):
- The Boolean value
Falseitself - The special value
None - Numeric zero in any form:
0,0.0,0j(complex zero) - Empty sequences:
""(empty string),[](empty list),()(empty tuple) - Empty mappings:
{}(empty dictionary) - Empty sets:
set()
Let's verify this with the bool() function:
# falsy_values.py
# All these values are falsy
print("Boolean False:", bool(False)) # Output: Boolean False: False
print("None:", bool(None)) # Output: None: False
print("Zero integer:", bool(0)) # Output: Zero integer: False
print("Zero float:", bool(0.0)) # Output: Zero float: False
print("Empty string:", bool("")) # Output: Empty string: False
print("Empty list:", bool([])) # Output: Empty list: False
print("Empty tuple:", bool(())) # Output: Empty tuple: False
print("Empty dict:", bool({})) # Output: Empty dict: False7.4.2) Truthy Values: Everything Else
Every other value in Python is considered truthy (it behaves like True in Boolean contexts). This includes:
- The Boolean value
Trueitself - Any non-zero number (positive or negative)
- Any non-empty string, list, tuple, dictionary, or set
- Most objects you create
# truthy_values.py
# All these values are truthy
print("Boolean True:", bool(True)) # Output: Boolean True: True
print("Positive integer:", bool(42)) # Output: Positive integer: True
print("Negative integer:", bool(-1)) # Output: Negative integer: True
print("Non-zero float:", bool(3.14)) # Output: Non-zero float: True
print("Non-empty string:", bool("Hello")) # Output: Non-empty string: True
print("String 'False':", bool("False")) # Output: String 'False': True
print("String '0':", bool("0")) # Output: String '0': True
print("Non-empty list:", bool([1, 2, 3])) # Output: Non-empty list: True
print("Non-empty tuple:", bool((1,))) # Output: Non-empty tuple: True
print("Non-empty dict:", bool({"a": 1})) # Output: Non-empty dict: TrueImportant note: The string "False" is truthy because it's a non-empty string. The string "0" is also truthy for the same reason. Only the actual Boolean value False and the numeric value 0 are falsy.
7.4.3) Why Truthiness Matters
Understanding truthiness is important because it's a fundamental concept in Python that you'll encounter frequently. In Chapter 8, you'll learn about if statements that make decisions based on conditions. These statements can use any value, not just explicit Boolean comparisons, because Python automatically evaluates the truthiness of values.
Truthiness allows you to write concise code that checks whether collections contain items, whether strings have content, or whether optional values are present. Here's a preview of how truthiness will be useful (we'll learn the full if statement syntax in Chapter 8):
# truthiness_preview.py
# Demonstrating truthiness with bool()
# (In Chapter 8, we'll use these directly in if statements)
# Checking if a string has content
user_input = ""
has_content = bool(user_input)
print("User entered something:", has_content) # Output: User entered something: False
user_input = "Alice"
has_content = bool(user_input)
print("User entered something:", has_content) # Output: User entered something: True
# Checking if a list has items
shopping_list = []
has_items = bool(shopping_list)
print("Shopping list has items:", has_items) # Output: Shopping list has items: False
shopping_list = ["milk", "eggs", "bread"]
has_items = bool(shopping_list)
print("Shopping list has items:", has_items) # Output: Shopping list has items: True
# Checking if a value exists (not None)
optional_value = None
value_exists = bool(optional_value)
print("Value exists:", value_exists) # Output: Value exists: FalseUnderstanding truthiness makes your code more Pythonic—it follows Python's conventions and idioms. When you see experienced Python programmers check conditions without explicit comparisons, they're leveraging truthiness to write cleaner, more readable code.
7.5) Chained Comparisons and Common Boolean Pitfalls
Python offers a powerful feature called chained comparisons that makes certain conditions more readable and closer to mathematical notation. However, this feature and other aspects of Boolean logic can also lead to common mistakes.
7.5.1) Chained Comparisons
In mathematics, you might write "10 < x < 20" to express that x is between 10 and 20. Python allows you to write this exact same way:
# chained_comparisons.py
# Checking if a value is in a range
x = 15
# The mathematical way (chained comparison)
in_range = 10 < x < 20
print("x is between 10 and 20:", in_range) # Output: x is between 10 and 20: True
# This is equivalent to combining two comparisons
# (We'll learn about 'and' in Chapter 9)
in_range = (10 < x) and (x < 20)
print("x is between 10 and 20:", in_range) # Output: x is between 10 and 20: True
# Testing with a value outside the range
y = 25
in_range = 10 < y < 20
print("y is between 10 and 20:", in_range) # Output: y is between 10 and 20: False
# Testing with boundary values
z = 10
in_range = 10 < z < 20
print("z is between 10 and 20:", in_range) # Output: z is between 10 and 20: False
# Including boundaries with <= and >=
in_range_inclusive = 10 <= z <= 20
print("z is between 10 and 20 (inclusive):", in_range_inclusive) # Output: z is between 10 and 20 (inclusive): TrueChained comparisons work by evaluating each pair of adjacent items. The expression 10 < x < 20 is evaluated as (10 < x) and (x < 20). All comparisons must be true for the entire chain to be true.
You can chain more than two comparisons:
# multiple_chains.py
# Chaining multiple comparisons
a = 5
b = 10
c = 15
d = 20
# Checking if values are in ascending order
ascending = a < b < c < d
print("Values are in ascending order:", ascending) # Output: Values are in ascending order: TrueHere's a visual representation of how chained comparisons work:
7.5.2) Common Pitfall: Assignment vs Comparison
One of the most common mistakes beginners make is using assignment (=) when they mean comparison (==):
# assignment_vs_comparison_pitfall.py
x = 10
# This is assignment, not comparison
# It assigns 20 to x, not compare x to 20
# x = 20 # This would change x to 20
# This is comparison
result = (x == 20) # This checks if x equals 20
print("x equals 20:", result) # Output: x equals 20: False
print("x is now:", x) # Output: x is now: 10
# In Chapter 8, you'll learn that using = in conditions causes an error
# if x = 20: # SyntaxError: invalid syntax
# print("This won't work")
# The correct comparison
# In Chapter 8, you'll write: if x == 10:
result = x == 10
print("x equals 10:", result) # Output: x equals 10: TruePython helps prevent this mistake in if statements (which you'll learn in Chapter 8) by raising a SyntaxError if you use assignment instead of comparison. However, in other contexts, using = when you meant == can create subtle bugs that are hard to find.
7.5.3) Common Pitfall: Floating-Point Comparisons
When comparing floating-point numbers, you need to be aware of precision issues:
# floating_point_comparison.py
# Floating-point arithmetic can have precision issues
result = 0.1 + 0.2
print("0.1 + 0.2 =", result) # Output: 0.1 + 0.2 = 0.30000000000000004
# Direct comparison might not work as expected
is_equal = (result == 0.3)
print("Result equals 0.3:", is_equal) # Output: Result equals 0.3: False
# The actual value is very close but not exactly 0.3
print("Difference:", result - 0.3) # Output: Difference: 5.551115123125783e-17
# For floating-point comparisons, use a small tolerance
tolerance = 0.0001
is_close = abs(result - 0.3) < tolerance
print("Result is close to 0.3:", is_close) # Output: Result is close to 0.3: True
# Python 3.5+ provides math.isclose() for this
# We'll learn about importing modules in Chapter 23
# import math
# is_close = math.isclose(result, 0.3)This issue arises because computers store floating-point numbers in binary, and some decimal numbers (like 0.1) cannot be represented exactly in binary. When you perform arithmetic with these numbers, tiny rounding errors accumulate. For most practical purposes, these errors are negligible, but they can cause direct equality comparisons to fail unexpectedly.
7.6) Booleans as Integers (1 and 0) and Why You Should Avoid Arithmetic
Here's a surprising fact: in Python, True and False are actually special cases of integers. True is equivalent to 1, and False is equivalent to 0. This is a historical artifact from Python's design, but it has some interesting implications. Understanding this relationship helps you avoid confusion, but you should rarely use it directly in your code.
7.6.1) Booleans Are Integers
You can verify that Booleans are integers by checking their type and performing integer operations:
# booleans_as_integers.py
# Booleans are a subtype of integers
print(isinstance(True, int)) # Output: True
print(isinstance(False, int)) # Output: True
# True equals 1, False equals 0
print(True == 1) # Output: True
print(False == 0) # Output: True
print(True == 2) # Output: False
# You can use Booleans in arithmetic (but you shouldn't!)
result = True + True
print("True + True =", result) # Output: True + True = 2
result = True + False
print("True + False =", result) # Output: True + False = 1
result = False * 100
print("False * 100 =", result) # Output: False * 100 = 0
# Booleans can be used as list indices
items = ["first", "second", "third"]
print(items[False]) # Output: first
print(items[True]) # Output: secondThe isinstance() function checks if a value is an instance of a type. When we check isinstance(True, int), Python returns True because the bool type is a subclass of the int type.
7.6.2) Why You Should Avoid Boolean Arithmetic
Even though Python allows you to use Booleans in arithmetic, you should avoid doing so. Using Booleans as numbers makes your code confusing and harder to understand. The relationship between Booleans and integers is mostly a historical detail that you should be aware of but rarely use directly.
Here's why Boolean arithmetic is problematic:
# boolean_arithmetic_problems.py
# Example of confusing code
count = 0
has_error = True
has_warning = False
# This works but is confusing
total = count + has_error + has_warning
print("Total:", total) # Output: Total: 1
# What does this mean? It's not clear!
# Better approach: be explicit
# We will learn about the if-else expression in Chapter 10.
error_count = 1 if has_error else 0
warning_count = 1 if has_warning else 0
total = count + error_count + warning_count
print("Total:", total) # Output: Total: 1The only common exception to this rule is when counting True values in a collection:
# counting_trues.py
# Counting how many conditions are true
conditions = [True, False, True, True, False]
# This is acceptable because the intent is clear
true_count = sum(conditions)
print("Number of true conditions:", true_count) # Output: Number of true conditions: 3
# This works because sum() adds up the values
# True is treated as 1, False as 0
# So sum([True, False, True, True, False]) = 1 + 0 + 1 + 1 + 0 = 3In this case, using sum() on a list of Booleans is a common Python idiom that's widely understood. The intent—counting how many conditions are true—is clear from the context.
7.6.3) Booleans in Type Conversion
Because Booleans are integers, converting them to integers explicitly is redundant:
# boolean_conversion.py
# Converting Booleans to integers (unnecessary)
value = True
int_value = int(value)
print("Integer value:", int_value) # Output: Integer value: 1
print("Are they equal?", value == int_value) # Output: Are they equal? True
# But converting integers to Booleans is useful
number = 0
bool_value = bool(number)
print("Boolean value:", bool_value) # Output: Boolean value: False
number = 42
bool_value = bool(number)
print("Boolean value:", bool_value) # Output: Boolean value: TrueConverting a Boolean to an integer with int() is unnecessary because Booleans already behave as integers. However, converting integers (or other types) to Booleans with bool() is useful when you want to explicitly check truthiness.
7.6.4) The Historical Reason
The reason Booleans are integers in Python is historical. Early versions of Python (before version 2.3) didn't have a separate Boolean type. Programmers used 1 for true and 0 for false. When the bool type was added, it was made a subclass of int to maintain backward compatibility with existing code.
Today, you should think of True and False as Boolean values first and foremost. Their relationship to 1 and 0 is an implementation detail that you rarely need to think about, except when you encounter it in unexpected places.
7.7) Using in and not in in Conditions
The in and not in operators test for membership—whether a value exists within a collection. These operators are incredibly useful for creating readable conditions, especially when working with strings, lists, and other collections.
7.7.1) Testing Membership in Strings
The in operator checks if one string appears within another string:
# string_membership.py
# Checking if a substring exists in a string
text = "Python programming is fun"
# Using 'in' to check for substrings
has_python = "Python" in text
print("Contains 'Python':", has_python) # Output: Contains 'Python': True
has_java = "Java" in text
print("Contains 'Java':", has_java) # Output: Contains 'Java': False
# Case-sensitive matching
has_python_lower = "python" in text
print("Contains 'python':", has_python_lower) # Output: Contains 'python': False
# Using 'not in' to check for absence
has_no_java = "Java" not in text
print("Does not contain 'Java':", has_no_java) # Output: Does not contain 'Java': TrueThe in operator performs a case-sensitive search. If you need case-insensitive searching, you can convert both strings to the same case:
# case_insensitive_search.py
text = "Python Programming"
# Case-sensitive search (doesn't match)
result = "python" in text
print("Contains 'python':", result) # Output: Contains 'python': False
# Case-insensitive search (convert both to lowercase)
result = "python" in text.lower()
print("Contains 'python' (case-insensitive):", result) # Output: Contains 'python' (case-insensitive): True
# The original string is unchanged
print("Original text:", text) # Output: Original text: Python Programming7.7.2) Testing Membership in Lists
The in operator also works with lists (which we'll learn about in detail in Chapter 14):
# list_membership.py
# Checking if a value exists in a list
numbers = [1, 2, 3, 4, 5]
has_three = 3 in numbers
print("List contains 3:", has_three) # Output: List contains 3: True
has_ten = 10 in numbers
print("List contains 10:", has_ten) # Output: List contains 10: False
# Using 'not in'
missing_ten = 10 not in numbers
print("List does not contain 10:", missing_ten) # Output: List does not contain 10: True
# Works with strings in lists
fruits = ["apple", "banana", "cherry"]
has_banana = "banana" in fruits
print("List contains 'banana':", has_banana) # Output: List contains 'banana': True
has_grape = "grape" in fruits
print("List contains 'grape':", has_grape) # Output: List contains 'grape': False7.7.3) Membership Testing with Ranges
You can also use in with range objects (which we'll learn about in Chapter 12):
# range_membership.py
# Checking if a number is in a range
age = 25
# Using 'in' with range
is_adult = age in range(18, 100)
print("Is adult:", is_adult) # Output: Is adult: True
# However, comparison operators are more efficient for numeric ranges
is_adult = 18 <= age < 100
print("Is adult:", is_adult) # Output: Is adult: True
# 'in' with range is useful for specific sequences
valid_ages = range(18, 66) # Working age range
is_working_age = age in valid_ages
print("Is working age:", is_working_age) # Output: Is working age: True7.7.4) The not in Operator
The not in operator is the opposite of in—it returns True if the value is NOT found in the collection:
# not_in_operator.py
# Using 'not in' for clearer logic
allowed_users = ["alice", "bob", "charlie"]
current_user = "eve"
# Using 'not in' is more readable
is_unauthorized = current_user not in allowed_users
print("User is unauthorized:", is_unauthorized) # Output: User is unauthorized: True
# Checking for missing required fields
provided_fields = ["name", "email"]
# Finding missing fields
missing_name = "name" not in provided_fields
missing_email = "email" not in provided_fields
missing_password = "password" not in provided_fields
print("Missing name:", missing_name) # Output: Missing name: False
print("Missing email:", missing_email) # Output: Missing email: False
print("Missing password:", missing_password) # Output: Missing password: TrueThe in and not in operators make your conditions more readable and expressive. Instead of writing complex comparisons, you can directly express "is this value in this collection?" which matches how you think about the problem.
In this chapter, you've learned the fundamentals of Boolean values and conditions in Python. You now understand:
- Boolean values
TrueandFalseand their role in decision-making - Comparison operators (
==,!=,<,>,<=,>=) for creating conditions - The
bool()function for converting values to Booleans - Truthiness and falsiness—how Python treats all values as either true or false
- Chained comparisons for readable range checks
- Common pitfalls to avoid when working with Booleans
- The relationship between Booleans and integers (and why to avoid Boolean arithmetic)
- Membership testing with
inandnot inoperators
These concepts form the foundation for the control flow structures you'll learn in the next chapters. In Chapter 8, you'll use Boolean expressions in if statements to make your programs respond to different conditions. In Chapter 9, you'll learn to combine multiple conditions using logical operators like and, or, and not. And in Chapters 10 and 11, you'll use conditions to control loops that repeat actions.
Understanding Booleans and conditions is essential for writing programs that can make decisions, validate input, and respond appropriately to different situations. Practice working with these concepts, and you'll find that decision-making in Python becomes natural and intuitive.