Skip to main content
Advertisement

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.

Advertisement