Back to List

Short-circuit Evaluation

Learn how Python's and/or operators are actually evaluated, why order matters, and practical patterns.

Intermediate
|
10min
|
Verified (2026-07)
short-circuit evaluationshort-circuitandorconditional execution
Progress0/18 (0%)

Short-Circuit Evaluation

After this topic, you will be able to:

Understand how Python's and/or operators work internally, use short-circuit evaluation in practical patterns, and explain why order matters.


What is Short-Circuit Evaluation?

Short-circuit evaluation is a logical operator optimization where the evaluation of an expression stops as soon as the result is known.

python
# and: if the first operand is False, the second is not evaluated
False and print("This never runs") # False
# or: if the first operand is True, the second is not evaluated
True or print("This never runs") # True

Why does it work this way? Logically, it makes sense:

  • and: If any part is False, the whole thing is False. If the first part is False, there's no need to check the second.
  • or: If any part is True, the whole thing is True. If the first part is True, there's no need to check the second.

How and Works

python
# and returns the first falsy value, or the last value
print(0 and 5) # 0 (0 is falsy β†’ stops here)
print("" and "hello") # "" (empty string is falsy)
print(None and 42) # None (None is falsy)
print(1 and 5) # 5 (1 is truthy β†’ goes to next β†’ returns 5)
print("hello" and 42) # 42 (both truthy β†’ returns the last value)
print(3 and 2 and 1) # 1 (all truthy β†’ returns the last value)
print(3 and 0 and 1) # 0 (stops at 0)

Key takeaway: and returns the first falsy value. If all values are truthy, it returns the last value.

Python's Falsy Values

python
# These all evaluate to False
bool(False) # False
bool(0) # False
bool(0.0) # False
bool("") # False
bool(None) # False
bool([]) # False (empty list)
bool({}) # False (empty dictionary)
# Everything else is True
bool(1) # True
bool("hello") # True
bool([1, 2]) # True

How or Works

python
# or returns the first truthy value, or the last value
print(0 or 5) # 5 (0 is falsy β†’ goes to next β†’ returns 5)
print("" or "hello") # "hello" (empty string is falsy β†’ returns "hello")
print(None or 42) # 42
print(1 or 5) # 1 (1 is truthy β†’ stops here)
print("hi" or "bye") # "hi" (first is truthy)
print(0 or "" or None) # None (all falsy β†’ returns the last value)
print(0 or "" or 42) # 42 (first truthy value)

Key takeaway: or returns the first truthy value. If all values are falsy, it returns the last value.


Practical Pattern: Setting Defaults

python
# If the user doesn't enter a name, use "Guest"
username = input("Enter name: ") or "Guest"
print(f"Hello, {username}")
# Set a default value if the configuration is missing
config = {}
db_host = config.get("host") or "localhost"
db_port = config.get("port") or 5432

This is a pattern for setting default values with or. If the left side is falsy (empty string, None, etc.), the right side is used. This is a common pattern in JavaScript as well.

Caution: This pattern can be dangerous if 0 or an empty string is a valid value.

python
# Bug: 0 is also falsy, so the default is applied
count = 0
result = count or 10 # 10 (should be 0)
# Fix: Explicit comparison
result = count if count is not None else 10 # 0

Practical Pattern: Conditional Execution

python
# Use `and` for conditional execution (alternative to `if`)
data = [1, 2, 3]
data and print(f"Data has {len(data)} items") # Prints
empty = []
empty and print("This won't print") # Doesn't print (empty list = falsy)
python
# Safe attribute access
user = {"name": "Hoon", "address": None}
# If `address` is None, don't call `.get()`
city = user.get("address") and user["address"].get("city")
print(city) # None (and stops at None)

Why Order Matters

The key to short-circuit evaluation is to place expensive operations later.

python
def is_valid_user(user_id):
"""DB lookup β€” slow operation"""
print(f"Querying DB for user {user_id}...")
return user_id > 0
def has_permission(user_id, action):
"""Permission check β€” even slower operation"""
print(f"Checking permission for {action}...")
return True
# Good: Checks the faster condition first
user_id = -1
if user_id > 0 and is_valid_user(user_id) and has_permission(user_id, "read"):
print("Access granted")
# Output: (Nothing printed β€” `user_id > 0` is False, so the DB is not queried)
# Bad: Checks the slower condition first
if is_valid_user(user_id) and user_id > 0:
print("Access granted")
# Output: "Querying DB for user -1..." (Unnecessary DB query)

It's also useful for preventing errors:

python
# Indexing an empty list causes an error
items = []
# Bad: Causes an IndexError
# if items[0] > 10:
# Good: Checks if the list is empty first
if items and items[0] > 10:
print("First item is large")
# `items` is falsy (empty list), so `items[0]` is not executed

Things to Keep in Mind When Debugging

Short-circuit evaluation can make debugging harder.

python
def check_permission(user):
print(f"Checking permission for {user['name']}")
return user.get("role") == "admin"
def validate_input(data):
print(f"Validating: {data}")
return len(data) > 0
user = {"name": "Hoon", "role": "admin"}
data = "hello"
if check_permission(user) or validate_input(data):
print("Granted")
# Output:
# Checking permission for Hoon
# Granted
# β†’ `validate_input` is not executed! (print statement is skipped)

In an or statement, if the first part is True, the second function is not even called. If you have a function with side effects (logging, incrementing a counter) in a conditional, its execution depends on the result of the first part. If the side effect needs to always run, call it outside the conditional.


Comparison with Other Languages

javascript
// JavaScript β€” same behavior
const name = userInput || "Guest";
const port = config.port || 3000;

// JavaScript ES2020: ?? (Nullish Coalescing)
const count = 0 ?? 10;  // 0 (?? only checks for null/undefined)
const count2 = 0 || 10;  // 10 (|| checks for all falsy values)
python
# Python doesn't have the ?? operator
# Instead, use explicit comparison
count = 0
result = count if count is not None else 10 # 0

JavaScript's ?? (Nullish Coalescing) only checks for null and undefined, so it's safe when 0 or an empty string is a valid value. Python doesn't have a corresponding operator, so you need to use an explicit is not None comparison.


Combining with not

python
# `not` doesn't use short-circuiting; it's a simple inversion
print(not True) # False
print(not 0) # True
print(not "") # True
print(not "hello") # False
python
# Precedence in complex conditions
# not > and > or
print(not True or False) # False (not True β†’ False, False or False β†’ False)
print(not (True or False)) # False (True or False β†’ True, not True β†’ False)
# Use parentheses explicitly for readability
if (age >= 18) and (not is_banned):
print("Access allowed")

Key Takeaways

OperatorShort-Circuit ConditionReturn Value
andFirst operand is falsyFirst falsy value, or the last value
orFirst operand is truthyFirst truthy value, or the last value
not(No short-circuiting)Always True or False

Understanding short-circuit evaluation gives you three benefits: setting default values (x or default), safe attribute access (items and items[0]), and performance optimization (putting faster conditions first). Most programming languages (JavaScript, Java, C++) follow the same rules, so once you learn it, you can apply it everywhere.

πŸ’¬ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...