Booleans and comparison logic are the foundation of every conditional in your FastAPI application โ checking whether a user is authenticated, whether a post is published, whether a request value is within an allowed range. Python’s approach to booleans is more expressive than most languages: logical operators are English words (and, or, not), the language distinguishes between equality (==) and identity (is), and every Python object has a truth value that can be used directly in conditions. Understanding short-circuit evaluation and identity vs equality is especially important when working with None values in SQLAlchemy database results.
Boolean Basics
# Python booleans: True and False (capitalised)
is_active = True
is_deleted = False
# bool is a subclass of int in Python
print(int(True)) # 1
print(int(False)) # 0
print(True + True) # 2 โ valid but unusual
print(True * 5) # 5
# Useful: count True values in a list
results = [True, False, True, True, False]
print(sum(results)) # 3 โ counts True values
and, or, not are spelled out as English words โ they are not &&, ||, ! as in JavaScript. This is one of the most common syntax errors for developers transitioning from JavaScript. The words feel natural once you get used to them: if is_admin and is_active: reads almost like English.or operator can replace simple ternary expressions for default values โ name = user_input or "Guest" returns user_input if it is truthy, otherwise "Guest". This pattern is common in FastAPI for providing default values: title = request.title or "Untitled". However, be careful when the value 0 or an empty string is a valid input โ falsy values would incorrectly trigger the default.is to compare with None, never ==. value is None checks identity (is this exactly the None object?), while value == None can be overridden by custom __eq__ methods. SQLAlchemy model attributes and FastAPI optional parameters return None when unset โ always check them with is None or is not None.Comparison Operators
x, y = 10, 20
x == y # False โ equal value
x != y # True โ not equal
x < y # True โ less than
x > y # False โ greater than
x <= y # True โ less than or equal
x >= y # False โ greater than or equal
# Python allows chained comparisons (unique to Python)
age = 25
18 <= age <= 65 # True โ equivalent to (18 <= age) and (age <= 65)
# Range checking (elegant Python idiom)
score = 87
if 0 <= score <= 100:
print("Valid score")
# String comparison โ lexicographic (alphabetical)
"apple" < "banana" # True
"alice" == "Alice" # False (case-sensitive)
"abc" < "abd" # True (compares char by char)
Logical Operators โ and, or, not
# Python uses English words, not symbols
# and โ both must be True
# or โ at least one must be True
# not โ negates the boolean value
is_admin = True
is_active = False
is_admin and is_active # False (both must be True)
is_admin or is_active # True (at least one is True)
not is_admin # False (negates True)
not is_active # True (negates False)
# Combining conditions
age = 25
has_id = True
if age >= 18 and has_id:
print("Entry allowed")
# not with parentheses for clarity
if not (age < 18 or not has_id):
print("Also entry allowed")
# De Morgan's laws โ equivalent forms:
# not (A and B) == (not A) or (not B)
# not (A or B) == (not A) and (not B)
Short-Circuit Evaluation
# Short-circuit: Python stops evaluating as soon as result is certain
# 'and' short-circuits on first False
# 'or' short-circuits on first True
def expensive():
print("Expensive called!")
return True
False and expensive() # "Expensive called!" is NOT printed
True or expensive() # "Expensive called!" is NOT printed
# Practical use: guard against None before accessing attribute
user = None
if user is not None and user.is_active: # safe โ doesn't crash if user is None
print("Active user")
# 'or' for default values (short-circuit)
name = "" or "Guest" # "Guest" โ empty string is falsy
name = "Alice" or "Guest" # "Alice" โ non-empty is truthy, stops here
# 'and' to return value only if condition is met
result = is_admin and "Admin Panel" # "Admin Panel" if admin, else False
Identity vs Equality โ is vs ==
# == checks VALUE equality
# is checks IDENTITY (same object in memory)
a = [1, 2, 3]
b = [1, 2, 3]
c = a
a == b # True โ same values
a is b # False โ different objects in memory
a is c # True โ c points to the SAME object as a
# ALWAYS use 'is' for None checks
value = None
value is None # True โ correct
value == None # True โ works but poor style, can be overridden
# ALWAYS use 'is' for True/False identity checks (rare)
# value is True # checks if it's the exact True object
# value == True # checks if value equals 1 (since bool is int)
# Python caches small integers โ don't rely on 'is' for numbers
x = 256; y = 256; x is y # True (cached)
x = 257; y = 257; x is y # False (not cached โ implementation detail!)
# Rule: use 'is' ONLY for None, True, and False
# Use '==' for all other value comparisons
Membership and Identity Operators
# Membership: 'in' and 'not in'
fruits = ["apple", "banana", "cherry"]
"apple" in fruits # True
"mango" in fruits # False
"mango" not in fruits # True
# Works on strings too
"ell" in "hello" # True
"xyz" not in "hello" # True
# Works on dicts (checks keys by default)
user = {"name": "Alice", "role": "admin"}
"role" in user # True (checks keys)
"admin" in user # False (does not check values by default)
"admin" in user.values() # True โ
# FastAPI use: check if a role is permitted
ALLOWED_ROLES = {"user", "editor", "admin"} # set โ O(1) lookup
def check_role(role: str) -> bool:
return role in ALLOWED_ROLES
Common Mistakes
Mistake 1 โ Using JavaScript logical operators
โ Wrong โ JavaScript-style operators:
if is_admin && is_active: # SyntaxError
if !is_deleted: # SyntaxError
โ Correct โ Python uses English words:
if is_admin and is_active: # โ
if not is_deleted: # โ
Mistake 2 โ Using == to check for None
โ Wrong โ equality check for None:
if result == None: # works but poor style
โ Correct โ identity check for None:
if result is None: # โ Pythonic, safe with custom __eq__
if result is not None: # โ for the positive case
Mistake 3 โ Relying on truthiness when 0 or "" are valid values
โ Wrong โ 0 and empty string are falsy, causes bugs:
count = 0
label = ""
name = count or "no items" # "no items" โ but 0 is a valid count!
title = label or "untitled" # "untitled" โ but "" might be intentional
โ Correct โ check explicitly with is None:
name = "no items" if count is None else count # โ 0 is kept
title = "untitled" if label is None else label # โ "" is kept
Quick Reference
| Operator | Python | JS Equivalent | Example |
|---|---|---|---|
| Logical AND | and |
&& |
a and b |
| Logical OR | or |
|| |
a or b |
| Logical NOT | not |
! |
not a |
| Equal value | == |
=== |
a == b |
| Not equal | != |
!== |
a != b |
| Identity | is |
n/a | x is None |
| Not identity | is not |
n/a | x is not None |
| Membership | in |
n/a | "a" in list |
| Not membership | not in |
n/a | "a" not in list |
| Chained compare | 0 <= x <= 100 |
n/a (use &&) | Range check |