Skip to main content

Ch 3.4 Conditional Expressions and the Walrus Operator

Python's conditional expressions (ternary operator) and the Walrus operator (:=) are powerful tools for making code more concise. However, they should only be used when they do not harm readability.

1. Conditional Expressions (Ternary Operator)​

Python's ternary operator compresses an if-else statement into a single-line expression.

# Basic syntax
# value_if_true if condition else value_if_false

x = 10
result = "positive" if x > 0 else "zero or negative"
print(result) # positive

# Comparison: regular if-else
if x > 0:
result = "positive"
else:
result = "zero or negative"

# Various usage examples
age = 20
label = "adult" if age >= 18 else "minor"
print(label) # adult

score = 75
grade = "pass" if score >= 60 else "fail"
print(grade) # pass

# Using in a function return value
def abs_value(n: int) -> int:
return n if n >= 0 else -n

print(abs_value(-5)) # 5
print(abs_value(3)) # 3

# Using as an expression, not just assignment
numbers = [1, -2, 3, -4, 5]
abs_numbers = [n if n >= 0 else -n for n in numbers]
print(abs_numbers) # [1, 2, 3, 4, 5]

Practical Examples​

from datetime import datetime

# Greeting based on current time
def get_greeting() -> str:
hour = datetime.now().hour
return (
"Good morning!" if hour < 12
else "Good afternoon!" if hour < 17
else "Good evening!" if hour < 22
else "It's late at night!"
)

print(get_greeting())

# Handling None
def format_phone(phone: str | None) -> str:
return phone if phone is not None else "No number"

print(format_phone("010-1234-5678")) # 010-1234-5678
print(format_phone(None)) # No number

# Inside list/dict comprehensions
users = [
{"name": "Alice", "score": 90},
{"name": "Bob", "score": 45},
{"name": "Charlie", "score": 72},
]

results = [
{"name": u["name"], "status": "pass" if u["score"] >= 60 else "fail"}
for u in users
]
for r in results:
print(f"{r['name']}: {r['status']}")
# Alice: pass
# Bob: fail
# Charlie: pass

2. Caution with Nested Ternary Operators​

score = 75

# Bad example — too many levels of nesting hurt readability
grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "D" if score >= 60 else "F"

# A regular if-elif is much more readable in this case
def get_grade(score: int) -> str:
if score >= 90:
return "A"
elif score >= 80:
return "B"
elif score >= 70:
return "C"
elif score >= 60:
return "D"
else:
return "F"

# Or use a dictionary mapping
def get_grade_v2(score: int) -> str:
thresholds = [(90, "A"), (80, "B"), (70, "C"), (60, "D")]
for threshold, grade in thresholds:
if score >= threshold:
return grade
return "F"

# A good nested example (two levels is acceptable)
x = 5
result = "positive" if x > 0 else ("negative" if x < 0 else "zero")
print(result) # positive

3. Walrus Operator (:=) — Python 3.8+ (PEP 572)​

The Walrus operator (:=) is also called an assignment expression. It assigns a value to a variable while simultaneously using that value as an expression.

The name comes from the resemblance of := to the eyes and tusks of a walrus.

# Basic usage
# Regular assignment: result = some_function() (statement)
# Walrus: result := some_function() (expression)

# Old way
n = len([1, 2, 3, 4, 5])
if n > 3:
print(f"The list has {n} elements, which is greater than 3.")

# Using the Walrus operator
data = [1, 2, 3, 4, 5]
if (n := len(data)) > 3:
print(f"The list has {n} elements, which is greater than 3.")
# n is computed only once but is available both in the condition and the body

4. Key Usage Patterns of the Walrus Operator​

Simplifying while Loops​

# Old way — reading a file
with open("data.txt", "r", encoding="utf-8") as f:
while True:
chunk = f.read(1024)
if not chunk:
break
process_data(chunk)

# More concise with the Walrus operator
with open("data.txt", "r", encoding="utf-8") as f:
while chunk := f.read(1024):
process_data(chunk)

# Processing user input
# Old way
while True:
user_input = input("Enter a command (type quit to exit): ")
if user_input.lower() == "quit":
break
print(f"Executing: {user_input}")

# With Walrus operator
while (user_input := input("Enter a command (type quit to exit): ")).lower() != "quit":
print(f"Executing: {user_input}")

Reusing an Intermediate Computation in List Comprehensions​

# Old way — same computation performed twice (inefficient)
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = [expensive(x) for x in data if expensive(x) > 5]

# With Walrus operator — computation done only once
def expensive(x: int) -> int:
"""Simulates a costly operation"""
return x * x + x // 2

result = [y for x in data if (y := expensive(x)) > 5]
print(result) # expensive() is called only once per element

# Real-world example: filter then transform
numbers = range(-5, 6)
positive_roots = [root for x in numbers if (root := x **0.5) > 0 and x > 0]
print(positive_roots) # [1.0, 1.4142..., 1.7320..., 2.0, 2.2360...]

Simplifying Nested Expressions​

# Checking for a dictionary key before processing
config = {"database": {"host": "localhost", "port": 5432}}

# Old way
db_config = config.get("database")
if db_config:
host = db_config.get("host")
if host:
print(f"DB host: {host}")

# With Walrus operator
if (db := config.get("database")) and (host := db.get("host")):
print(f"DB host: {host}")

5. Difference Between Expression and Statement​

# Expression: code that produces a value
# → can be assigned to a variable or passed as a function argument

x = 5 + 3 # 5 + 3 is an expression → 8
result = max(1, 2) # max(1, 2) is an expression → 2
flag = x > 0 # x > 0 is an expression → True

# Statement: code that executes but does not produce a value
# → cannot be assigned to a variable or passed as a function argument

x = 10 # assignment statement
if x > 0: # if statement
pass
for i in range(5): # for statement
pass

# The Walrus operator turns an assignment into an expression
# Regular assignment (=) is a statement, but Walrus (:=) is an expression

# This is a syntax error (= is a statement)
# if x = len(data): ...

# This works (:= is an expression)
data = [1, 2, 3]
if n := len(data):
print(f"{n} items of data")

6. Practical Examples​

Processing Regex Match Results with Walrus​

import re

texts = [
"Phone: 010-1234-5678",
"Name: John Doe",
"Email: alice@example.com",
"Contact: 02-999-0000",
"Address: 123 Main Street",
]

phone_pattern = re.compile(r"\d{2,3}-\d{3,4}-\d{4}")

# Old way
for text in texts:
match = phone_pattern.search(text)
if match:
print(f"Phone number found: {match.group()}")

# More concise with Walrus operator
for text in texts:
if match := phone_pattern.search(text):
print(f"Phone number found: {match.group()}")

# Output:
# Phone number found: 010-1234-5678
# Phone number found: 02-999-0000

Reading File Chunks (Production Pattern)​

def process_large_file(filename: str, chunk_size: int = 8192) -> int:
"""Processes a large file in chunks."""
total_bytes = 0

with open(filename, "rb") as f:
while data := f.read(chunk_size):
# Process data
total_bytes += len(data)

return total_bytes

# Process file line by line
def process_lines(filename: str) -> list[str]:
"""Processes only non-empty lines from a file."""
results = []
with open(filename, "r", encoding="utf-8") as f:
while line := f.readline():
if stripped := line.strip(): # skip empty lines after stripping whitespace
results.append(stripped.upper())
return results

Pro Tips: Avoid Walrus Overuse — Readability Comes First

The Walrus operator is powerful, but overuse can actually make code harder to understand.

# Bad example — Walrus overuse hurts readability
# It's hard to understand at a glance what this code does
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = [(y, z) for x in data if (y := x * 2) > 5 if (z := y + 1) < 15]
print(result)

# Good example — clear step-by-step processing
result = []
for x in data:
y = x * 2
if y > 5:
z = y + 1
if z < 15:
result.append((y, z))
print(result)

# Another good example — appropriate Walrus usage
def validate_and_process(items: list[str]) -> list[str]:
"""
Validates and processes items.
An example of using Walrus appropriately.
"""
return [
cleaned
for item in items
if (cleaned := item.strip()) and len(cleaned) > 2
]

items = [" hello ", " hi ", " ", "world", "ok"]
print(validate_and_process(items)) # ['hello', 'world']

# Decision criteria:
# Good use of Walrus: avoiding duplicate computation, simplifying while loops
# Regular if statement is better: when nesting becomes deep, or expressions are complex

Criteria for choosing between expression and statement:

# Simple and clear → conditional expression is fine
status = "active" if user.is_active else "inactive"

# Complex logic → use an if statement
if user.is_active and user.email_verified and not user.is_banned:
send_newsletter(user)
elif user.is_active and not user.email_verified:
send_verification_email(user)
else:
log_inactive_user(user)

Chapter 3 covered Python's various operators and expressions — from arithmetic, comparison, and logical operators to bitwise operations, advanced string usage, and conditional expressions with the Walrus operator. Chapter 4 covers how to control program flow using conditional statements and loops.