🌱 Beginner

Booleans & Comparisons

📖 Lesson 05 ⏱ 20 min 🧪 5 questions 💻 3 exercises

🎯 Learning Objectives

  • Understand the bool type and its two values: True and False
  • Combine conditions with logical operators: and, or, not
  • Understand short-circuit evaluation and how Python uses it
  • Distinguish == (equality) from is (identity)
  • Know which values are truthy vs falsy
  • Use boolean expressions in real-world validation patterns

The bool Type

Python's boolean type has exactly two values: True and False (always capitalised). Booleans are the result of comparisons and conditions — they're the foundation of all decision-making in code.

is_active = True
is_admin = False

print(type(is_active))  # <class 'bool'>

# bool is a subclass of int!
print(True + True)    # 2
print(False * 100)    # 0
print(int(True))      # 1
print(int(False))     # 0
bool_basics.py
bool is a subclass of int in Python. True is literally 1 and False is 0. This means you can use booleans in arithmetic — useful for counting how many conditions are True.

Comparison Operators

We saw these in Lesson 04. Here's the full set — each returns a bool:

OperatorMeaningExampleResult
==Equal to"hi" == "hi"True
!=Not equal to5 != 3True
<Less than3 < 5True
>Greater than5 > 5False
<=Less than or equal5 <= 5True
>=Greater than or equal7 >= 3True
age = 25

print(age == 25)     # True
print(age != 30)     # True
print(age >= 18)     # True

# Chained comparisons
print(18 <= age < 65)    # True — "working age"
print(0 < age < 18)     # False

# Strings compare lexicographically (dictionary order)
print("apple" < "banana")    # True
print("Zebra" < "apple")    # True (uppercase < lowercase in ASCII)
comparisons.py

Logical Operators: and, or, not

Logical operators combine boolean expressions:

OperatorMeaningReturns True when…
andLogical ANDBoth sides are True
orLogical ORAt least one side is True
notLogical NOTThe operand is False
age = 25
has_license = True
is_banned = False

# and — both must be True
can_drive = age >= 16 and has_license
print(can_drive)  # True

# or — at least one must be True
gets_discount = age < 12 or age >= 65
print(gets_discount)  # False

# not — flips the boolean
is_allowed = not is_banned
print(is_allowed)  # True

# Combining all three
can_enter = (age >= 18 or has_license) and not is_banned
print(can_enter)  # True
logical_ops.py

Truth tables

ABA and BA or Bnot A
TrueTrueTrueTrueFalse
TrueFalseFalseTrueFalse
FalseTrueFalseTrueTrue
FalseFalseFalseFalseTrue
Precedence: not binds tightest, then and, then or. When in doubt, use parentheses: (a or b) and c is clearer than relying on precedence.

Short-Circuit Evaluation

Python's and and or are short-circuit operators — they stop evaluating as soon as the result is determined:

  • and stops at the first falsy value (if everything is truthy, returns the last).
  • or stops at the first truthy value (if everything is falsy, returns the last).
# and — returns first falsy, or last value if all truthy
print(1 and 2 and 3)    # 3 (all truthy → last value)
print(1 and 0 and 3)    # 0 (first falsy)
print("" and "hello")   # "" (first falsy)

# or — returns first truthy, or last value if all falsy
print(0 or "" or "hi")  # "hi" (first truthy)
print(0 or "" or [])    # [] (all falsy → last value)

# Practical: default values
name = user_input or "Anonymous"
# If user_input is empty/None/falsy, use "Anonymous"
short_circuit.py
and / or don't always return True or False — they return the actual value that determined the result. This is why 0 or "default" returns "default", not True.
Short-circuit means side effects may not execute: False and print("hello") — the print() never runs because and already knows the result is falsy.

Identity (is) vs Equality (==)

These are two fundamentally different checks:

OperatorChecksQuestion it answers
==Equality (value)Do these have the same value?
isIdentity (memory)Are these the exact same object?
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)   # True — same value
print(a is b)   # False — different objects in memory
print(a is c)   # True — c points to the same object as a

# Use 'is' only for singletons: None, True, False
x = None
print(x is None)      # ✓ correct
print(x == None)      # works but discouraged by PEP 8
identity.py
Rule of thumb: Use == for everything except checking None. Use is None / is not None for None checks.

Truthy & Falsy Values

Every Python object can be evaluated as a boolean. These values are falsy:

Falsy ValueType
Falsebool
NoneNoneType
0, 0.0, 0jint / float / complex
"" (empty string)str
[] (empty list)list
() (empty tuple)tuple
{} (empty dict)dict
set() (empty set)set

Everything else is truthy — any non-zero number, non-empty container, or object.

# Pythonic: use truthiness directly
items = []

# Don't do this:
if len(items) == 0:
    print("Empty")

# Do this instead:
if not items:
    print("Empty")

# Check if a string has content
name = "Alice"
if name:
    print(f"Hello, {name}")

# Check if a value exists
result = None
if result is None:
    print("No result yet")
truthy_falsy.py
Idiomatic Python uses truthiness checks directly: if items: instead of if len(items) > 0:. This is shorter, faster, and more Pythonic.

Practical Boolean Patterns

# Pattern 1: Validation
def is_valid_age(age):
    return isinstance(age, int) and 0 <= age <= 150

# Pattern 2: Default with 'or'
username = input("Username: ") or "guest"

# Pattern 3: Guard clause with 'and'
# Only access .name if user is not None
display = user and user.name

# Pattern 4: Counting True values
scores = [85, 92, 45, 78, 61, 33]
passing = sum(s >= 60 for s in scores)
print(f"{passing} students passed")  # 4

# Pattern 5: all() and any()
numbers = [2, 4, 6, 8]
print(all(n % 2 == 0 for n in numbers))  # True — all even
print(any(n > 7 for n in numbers))       # True — at least one > 7
patterns.py
🤖

Ask your AI tutor! Confused about when and returns a value vs a boolean? Unsure why [] is [] is False? These subtleties matter — ask and get clarity.

💻 Exercises

01 Access Checker

Write a script that checks if a user can access a resource. They need to be: at least 18 years old AND (a member OR an admin). Test with several combinations and print the result.

Show solution
age = 25
is_member = True
is_admin = False

can_access = age >= 18 and (is_member or is_admin)
print(f"Access granted: {can_access}")  # True

# Test: under 18
age = 16
can_access = age >= 18 and (is_member or is_admin)
print(f"Access granted: {can_access}")  # False

# Test: 18+ but neither member nor admin
age = 30
is_member = False
is_admin = False
can_access = age >= 18 and (is_member or is_admin)
print(f"Access granted: {can_access}")  # False
02 Truthy Tester

Create a list of 10 different values (mix of numbers, strings, lists, None, booleans). Loop through them and print whether each is truthy or falsy using bool().

Show solution
values = [0, 1, "", "hello", [], [1, 2], None, True, False, 0.0]

for val in values:
    label = "truthy" if bool(val) else "falsy"
    print(f"{str(val):>10} → {label}")

# Output:
#          0 → falsy
#          1 → truthy
#            → falsy
#      hello → truthy
#         [] → falsy
#     [1, 2] → truthy
#       None → falsy
#       True → truthy
#      False → falsy
#        0.0 → falsy
03 Password Validator

Write a password strength checker. A password is "strong" if it meets all of these: at least 8 characters, contains at least one digit, and contains at least one uppercase letter. Use any() and string methods.

Show solution
password = "Hello123"

long_enough = len(password) >= 8
has_digit = any(c.isdigit() for c in password)
has_upper = any(c.isupper() for c in password)

is_strong = long_enough and has_digit and has_upper
print(f"Password: {password}")
print(f"Strong: {is_strong}")  # True

# Weak example
password2 = "hello"
long_enough = len(password2) >= 8
has_digit = any(c.isdigit() for c in password2)
has_upper = any(c.isupper() for c in password2)
is_strong = long_enough and has_digit and has_upper
print(f"\nPassword: {password2}")
print(f"Strong: {is_strong}")  # False