When validating input or processing data, you often need to compute a value, check a condition on it, and then use that value. Without the walrus operator, you either repeat the computation or clutter your code with temporary variables. The := operator solves this by combining assignment and expression in one step.

Any time you find yourself calling the same function twice to check and use a result, consider the walrus operator.

When Not to Use

walrus_if_statements.py
"""Walrus operator in if statements"""

# Basic walrus in if
print("Basic walrus in if:")

# Without walrus
data = 
length = len(data)
if length > 3:
    print(f"Large list: {length} items")

# With walrus - assign and check in one line
if (n := len(data)) > 3:
    print(f"Large list (walrus): {n} items")

# Avoid repeated calls
print("\nAvoid repeated calls:")

def expensive_computation():
    print("  Computing...")
    return 42

# Without walrus - calls function twice
if expensive_computation() > 40:
    result = expensive_computation()
    print(f"Result: {result}")

# With walrus - calls function once
if (result := expensive_computation()) > 40:
    print(f"Result (walrus): {result}")

# String processing
print("\nString processing:")

text = "Hello World"

# Check length and use it
if (length := len(text)) > 5:
    print(f"Long text ({length} chars): {text}")

# Check if text and get first word
if (first_word := text.split()[0] if text else None):
    print(f"First word: {first_word}")

# Input validation
print("\nInput validation:")

def get_username():
    return "alice"

# Validate and use input
if (username := get_username()) and len(username) >= 3:
    print(f"Valid username: {username}")
else:
    print("Invalid username")

# Chained conditions
print("\nChained conditions:")

data = {"score": 85, "grade": "B"}

# Check and extract value
if (score := data.get("score")) and score >= 80:
    print(f"Good score: {score}")

if (grade := data.get("grade")) and grade in ["A", "B"]:
    print(f"Passing grade: {grade}")

# Pattern matching with walrus
print("\nPattern matching with walrus:")

def parse_command(cmd):
    parts = cmd.split()
    return parts[0] if parts else None

command = "git commit -m 'message'"

if (cmd := parse_command(command)) == "git":
    print(f"Git command detected: {command}")

# Nested conditions
print("\nNested conditions:")

user = {"name": "Alice", "profile": {"age": 30}}

# Extract nested value
if (profile := user.get("profile")) and (age := profile.get("age")) >= 18:
    print(f"Adult user: {user['name']}, age {age}")

# Range checking
print("\nRange checking:")

numbers = [5, 2, 8, 1, 9, 3, 7]

# Check and use max value
if (max_val := max(numbers)) > 5:
    print(f"Max value {max_val} exceeds threshold")

# Check and use min value
if (min_val := min(numbers)) < 3:
    print(f"Min value {min_val} below threshold")

# File checking
print("\nFile checking:")

def read_config():
    return {"debug": True, "port": 8080}

# Check config and extract value
if (config := read_config()) and config.get("debug"):
    print(f"Debug mode enabled (port: {config.get('port')})")

# Practical example
print("\nPractical example:")

# Validate user input
def validate_email(email):
    return "@" in email and "." in email

email = "alice@example.com"
if (is_valid := validate_email(email)):
    print(f"Email '{email}' is valid")
    # Use is_valid later if needed

# Check and process result
def find_user(user_id):
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

if (user := find_user(1)):
    print(f"Found user: {user}")
else:
    print("User not found")

# Data processing pipeline
text = "  Hello World  "
if (cleaned := text.strip()) and (upper := cleaned.upper()) != cleaned:
    print(f"Cleaned and checked: '{cleaned}' -> '{upper}'")

"""Walrus operator in if statements"""

# Basic walrus in if
print("Basic walrus in if:")

# Without walrus
data = 
length = len(data)
if length > 3:
    print(f"Large list: {length} items")

# With walrus - assign and check in one line
if (n := len(data)) > 3:
    print(f"Large list (walrus): {n} items")

# Avoid repeated calls
print("\nAvoid repeated calls:")

def expensive_computation():
    print("  Computing...")
    return 42

# Without walrus - calls function twice
if expensive_computation() > 40:
    result = expensive_computation()
    print(f"Result: {result}")

# With walrus - calls function once
if (result := expensive_computation()) > 40:
    print(f"Result (walrus): {result}")

# String processing
print("\nString processing:")

text = "Hello World"

# Check length and use it
if (length := len(text)) > 5:
    print(f"Long text ({length} chars): {text}")

# Check if text and get first word
if (first_word := text.split()[0] if text else None):
    print(f"First word: {first_word}")

# Input validation
print("\nInput validation:")

def get_username():
    return "alice"

# Validate and use input
if (username := get_username()) and len(username) >= 3:
    print(f"Valid username: {username}")
else:
    print("Invalid username")

# Chained conditions
print("\nChained conditions:")

data = {"score": 85, "grade": "B"}

# Check and extract value
if (score := data.get("score")) and score >= 80:
    print(f"Good score: {score}")

if (grade := data.get("grade")) and grade in ["A", "B"]:
    print(f"Passing grade: {grade}")

# Pattern matching with walrus
print("\nPattern matching with walrus:")

def parse_command(cmd):
    parts = cmd.split()
    return parts[0] if parts else None

command = "git commit -m 'message'"

if (cmd := parse_command(command)) == "git":
    print(f"Git command detected: {command}")

# Nested conditions
print("\nNested conditions:")

user = {"name": "Alice", "profile": {"age": 30}}

# Extract nested value
if (profile := user.get("profile")) and (age := profile.get("age")) >= 18:
    print(f"Adult user: {user['name']}, age {age}")

# Range checking
print("\nRange checking:")

numbers = [5, 2, 8, 1, 9, 3, 7]

# Check and use max value
if (max_val := max(numbers)) > 5:
    print(f"Max value {max_val} exceeds threshold")

# Check and use min value
if (min_val := min(numbers)) < 3:
    print(f"Min value {min_val} below threshold")

# File checking
print("\nFile checking:")

def read_config():
    return {"debug": True, "port": 8080}

# Check config and extract value
if (config := read_config()) and config.get("debug"):
    print(f"Debug mode enabled (port: {config.get('port')})")

# Practical example
print("\nPractical example:")

# Validate user input
def validate_email(email):
    return "@" in email and "." in email

email = "alice@example.com"
if (is_valid := validate_email(email)):
    print(f"Email '{email}' is valid")
    # Use is_valid later if needed

# Check and process result
def find_user(user_id):
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

if (user := find_user(1)):
    print(f"Found user: {user}")
else:
    print("User not found")

# Data processing pipeline
text = "  Hello World  "
if (cleaned := text.strip()) and (upper := cleaned.upper()) != cleaned:
    print(f"Cleaned and checked: '{cleaned}' -> '{upper}'")

"""Walrus operator in if statements"""

# Basic walrus in if
print("Basic walrus in if:")

# Without walrus
data = 
length = len(data)
if length > 3:
    print(f"Large list: {length} items")

# With walrus - assign and check in one line
if (n := len(data)) > 3:
    print(f"Large list (walrus): {n} items")

# Avoid repeated calls
print("\nAvoid repeated calls:")

def expensive_computation():
    print("  Computing...")
    return 42

# Without walrus - calls function twice
if expensive_computation() > 40:
    result = expensive_computation()
    print(f"Result: {result}")

# With walrus - calls function once
if (result := expensive_computation()) > 40:
    print(f"Result (walrus): {result}")

# String processing
print("\nString processing:")

text = "Hello World"

# Check length and use it
if (length := len(text)) > 5:
    print(f"Long text ({length} chars): {text}")

# Check if text and get first word
if (first_word := text.split()[0] if text else None):
    print(f"First word: {first_word}")

# Input validation
print("\nInput validation:")

def get_username():
    return "alice"

# Validate and use input
if (username := get_username()) and len(username) >= 3:
    print(f"Valid username: {username}")
else:
    print("Invalid username")

# Chained conditions
print("\nChained conditions:")

data = {"score": 85, "grade": "B"}

# Check and extract value
if (score := data.get("score")) and score >= 80:
    print(f"Good score: {score}")

if (grade := data.get("grade")) and grade in ["A", "B"]:
    print(f"Passing grade: {grade}")

# Pattern matching with walrus
print("\nPattern matching with walrus:")

def parse_command(cmd):
    parts = cmd.split()
    return parts[0] if parts else None

command = "git commit -m 'message'"

if (cmd := parse_command(command)) == "git":
    print(f"Git command detected: {command}")

# Nested conditions
print("\nNested conditions:")

user = {"name": "Alice", "profile": {"age": 30}}

# Extract nested value
if (profile := user.get("profile")) and (age := profile.get("age")) >= 18:
    print(f"Adult user: {user['name']}, age {age}")

# Range checking
print("\nRange checking:")

numbers = [5, 2, 8, 1, 9, 3, 7]

# Check and use max value
if (max_val := max(numbers)) > 5:
    print(f"Max value {max_val} exceeds threshold")

# Check and use min value
if (min_val := min(numbers)) < 3:
    print(f"Min value {min_val} below threshold")

# File checking
print("\nFile checking:")

def read_config():
    return {"debug": True, "port": 8080}

# Check config and extract value
if (config := read_config()) and config.get("debug"):
    print(f"Debug mode enabled (port: {config.get('port')})")

# Practical example
print("\nPractical example:")

# Validate user input
def validate_email(email):
    return "@" in email and "." in email

email = "alice@example.com"
if (is_valid := validate_email(email)):
    print(f"Email '{email}' is valid")
    # Use is_valid later if needed

# Check and process result
def find_user(user_id):
    users = {1: "Alice", 2: "Bob"}
    return users.get(user_id)

if (user := find_user(1)):
    print(f"Found user: {user}")
else:
    print("User not found")

# Data processing pipeline
text = "  Hello World  "
if (cleaned := text.strip()) and (upper := cleaned.upper()) != cleaned:
    print(f"Cleaned and checked: '{cleaned}' -> '{upper}'")

walrus_while_loops.py
"""Walrus operator in while loops"""

# Basic while with walrus
print("Basic while with walrus:")

# Without walrus
data = [1, 2, 3, 4, 5]
index = 0
while index < len(data):
    item = data[index]
    print(f"  Item: {item}")
    index += 1

# With walrus - cleaner iteration pattern
print("\nWith walrus:")
items = iter([10, 20, 30, 40, 50])
while (item := next(items, None)) is not None:
    print(f"  Item: {item}")

# Reading lines
print("\nReading lines:")

# Simulate file reading
lines = iter(["Line 1", "Line 2", "Line 3", ""])

while (line := next(lines, None)) is not None and line != "":
    print(f"  Processing: {line}")

# Input loop
print("\nInput loop:")

# Simulate user input
inputs = iter(["hello", "world", "quit"])

def get_input():
    return next(inputs, None)

while (user_input := get_input()) != "quit":
    if user_input:
        print(f"  You entered: {user_input}")

# Chunk processing
print("\nChunk processing:")

def read_chunk(data, chunk_size):
    """Generator that yields chunks"""
    for i in range(0, len(data), chunk_size):
        yield data[i:i + chunk_size]

data = list(range(1, 11))
chunks = read_chunk(data, 3)

while (chunk := next(chunks, None)) is not None:
    print(f"  Processing chunk: {chunk}")

# Accumulator pattern
print("\nAccumulator pattern:")

numbers = [5, 10, 15, 20, 25, 30]
index = 0
total = 0

while (index < len(numbers)) and ((current := numbers[index]) < 25):
    total += current
    print(f"  Added {current}, total: {total}")
    index += 1

# Queue processing
print("\nQueue processing:")

queue = [1, 2, 3, 4, 5]

while queue and (item := queue.pop(0)) < 4:
    print(f"  Processing: {item}")
    
print(f"Remaining: {queue}")

# Stream processing
print("\nStream processing:")

def generate_data():
    """Simulate data stream"""
    for i in range(1, 6):
        yield i * 10

stream = generate_data()

while (value := next(stream, None)) is not None:
    if value > 30:
        print(f"  Value {value} exceeds threshold")
    else:
        print(f"  Value {value} within range")

# Parse until delimiter
print("\nParse until delimiter:")

tokens = iter(["token1", "token2", "STOP", "token3", "token4"])

result = []
while (token := next(tokens, None)) and token != "STOP":
    result.append(token)

print(f"Tokens before STOP: {result}")

# Buffer reading
print("\nBuffer reading:")

buffer = [b'Hello', b' ', b'World', b'']

output = []
while (chunk := buffer.pop(0) if buffer else None):
    output.append(chunk)

print(f"Read: {b''.join(output).decode()}")

# Practical example
print("\nPractical example:")

# Process commands until exit
commands = iter(["print hello", "print world", "count 5", "exit"])

while (cmd := next(commands, None)) and not cmd.startswith("exit"):
    parts = cmd.split()
    action = parts[0]
    
    if action == "print":
        print(f"  Output: {' '.join(parts[1:])}")
    elif action == "count":
        count = int(parts[1])
        print(f"  Count: {count}")

# Process data with validation
data_stream = iter([{"value": 10}, {"value": 20}, {"value": -1}, {"value": 30}])

total = 0
while (item := next(data_stream, None)) and (val := item.get("value", -1)) >= 0:
    total += val
    print(f"  Added {val}, running total: {total}")

print(f"Final total: {total}")

# Read until condition
numbers = iter([1, 3, 5, 7, 9, 11, 13])
collected = []

while (n := next(numbers, None)) is not None and n < 10:
    collected.append(n)

print(f"Collected (< 10): {collected}")

walrus_comprehensions.py
"""Walrus operator in comprehensions"""

# Basic list comprehension
print("Basic list comprehension:")

# Without walrus - compute twice
numbers = [1, 2, 3, 4, 5]
squared = [n * n for n in numbers if n * n > 10]
print(f"Squared (>10): {squared}")

# With walrus - compute once
squared_walrus = [sq for n in numbers if (sq := n * n) > 10]
print(f"Squared walrus: {squared_walrus}")

# Expensive computation
print("\nExpensive computation:")

def process(n):
    print(f"  Processing {n}")
    return n * 10

numbers = [1, 2, 3, 4, 5]

# Without walrus - processes each number twice
result = [process(n) for n in numbers if process(n) > 25]
print(f"Result (called twice): {result}")

# With walrus - processes once
result_walrus = [p for n in numbers if (p := process(n)) > 25]
print(f"Result walrus (called once): {result_walrus}")

# String processing
print("\nString processing:")

words = ["hello", "world", "python", "programming"]

# Get uppercase versions only for words longer than 5
result = [upper for word in words if (upper := word.upper()) and len(word) > 5]
print(f"Long words (uppercase): {result}")

# Data transformation
print("\nData transformation:")

data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Square numbers and filter
squared_evens = [sq for n in data if (sq := n * n) % 2 == 0]
print(f"Even squares: {squared_evens}")

# Dict comprehension
print("\nDict comprehension:")

numbers = [1, 2, 3, 4, 5]

# Create dict with computed values
squares_dict = {n: sq for n in numbers if (sq := n * n) < 20}
print(f"Squares dict: {squares_dict}")

# Nested comprehension
print("\nNested comprehension:")

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Flatten and filter with transformation
result = [doubled for row in matrix for item in row if (doubled := item * 2) > 8]
print(f"Filtered doubled: {result}")

# Generator expression
print("\nGenerator expression:")

numbers = range(1, 11)

# Generator with walrus
gen = (sq for n in numbers if (sq := n * n) > 20)
result = list(gen)
print(f"Squares > 20: {result}")

# Any/All with walrus
print("\nAny/All with walrus:")

data = [1, 2, 3, 4, 5]

# Check if any squared value > 20
if any((sq := n * n) > 20 for n in data):
    print(f"Found square > 20")

# Get first match with walrus
numbers = [1, 2, 3, 4, 5, 6, 7]
first_big = next((sq for n in numbers if (sq := n * n) > 20), None)
print(f"First square > 20: {first_big}")

# Set comprehension
print("\nSet comprehension:")

words = ["hello", "world", "hello", "python", "world"]

# Get unique lengths
lengths = {ln for word in words if (ln := len(word)) > 4}
print(f"Unique lengths > 4: {lengths}")

# Multiple conditions
print("\nMultiple conditions:")

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Multiple transformations
result = [
    doubled 
    for n in numbers 
    if (doubled := n * 2) > 5 
    if (squared := doubled * doubled) < 200
]
print(f"Filtered transformations: {result}")

# Practical example
print("\nPractical example:")

# Process user data
users = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35}
]

# Get names of users over 26
names = [name for user in users if (age := user.get("age", 0)) > 26 and (name := user.get("name"))]
print(f"Users over 26: {names}")

# Parse and validate
raw_data = ["10", "20", "abc", "30", "def"]

def try_parse(s):
    try:
        return int(s)
    except ValueError:
        return None

# Get valid integers > 15
valid = [num for s in raw_data if (num := try_parse(s)) is not None and num > 15]
print(f"Valid numbers > 15: {valid}")

# File processing simulation
lines = ["# comment", "data: 10", "# ignore", "data: 20", "data: 30"]

# Extract data lines and parse
data_values = [
    int(parts[1])
    for line in lines
    if not line.startswith("#")
    if (parts := line.split(": "))
    if len(parts) == 2
]
print(f"Data values: {data_values}")

walrus_avoid_repeated_calls.py
"""Walrus operator to avoid repeated calls"""

# Avoiding double computation
print("Avoiding double computation:")

def expensive_function(x):
    print(f"  Computing for {x}...")
    return x ** 3

# Without walrus - calls function twice
x = 5
if expensive_function(x) > 100:
    result = expensive_function(x)
    print(f"Result: {result}")

# With walrus - calls once
x = 5
if (result := expensive_function(x)) > 100:
    print(f"Result (walrus): {result}")

# Method calls
print("\nMethod calls:")

text = "  Hello World  "

# Without walrus - strip called twice
if len(text.strip()) > 5:
    cleaned = text.strip()
    print(f"Cleaned: '{cleaned}'")

# With walrus - strip called once
if len(cleaned := text.strip()) > 5:
    print(f"Cleaned (walrus): '{cleaned}'")

# Regex matching
print("\nRegex matching:")

import re

text = "Error: File not found"

# Without walrus - match called twice
pattern = r"Error: (.*)"
if re.match(pattern, text):
    match = re.match(pattern, text)
    print(f"Error message: {match.group(1)}")

# With walrus - match called once
if (match := re.match(pattern, text)):
    print(f"Error message (walrus): {match.group(1)}")

# Database query
print("\nDatabase query:")

def find_user(user_id):
    print(f"  Querying database for user {user_id}...")
    users = {1: {"name": "Alice", "email": "alice@example.com"}}
    return users.get(user_id)

# Without walrus - queries twice
user_id = 1
if find_user(user_id):
    user = find_user(user_id)
    print(f"User: {user}")

# With walrus - queries once
if (user := find_user(user_id)):
    print(f"User (walrus): {user}")

# List operations
print("\nList operations:")

numbers = [1, 2, 3, 4, 5]

# Without walrus - sum called multiple times
if sum(numbers) > 10:
    total = sum(numbers)
    avg = total / len(numbers)
    print(f"Average: {avg}")

# With walrus - sum called once
if (total := sum(numbers)) > 10:
    avg = total / len(numbers)
    print(f"Average (walrus): {avg}")

# File operations
print("\nFile operations:")

def read_config():
    print("  Reading config file...")
    return {"debug": True, "port": 8080, "host": "localhost"}

# Without walrus - reads twice
if "debug" in read_config():
    config = read_config()
    print(f"Debug enabled on {config['host']}:{config['port']}")

# With walrus - reads once
if (config := read_config()) and "debug" in config:
    print(f"Debug enabled (walrus) on {config['host']}:{config['port']}")

# JSON parsing
print("\nJSON parsing:")

import json

def parse_json(json_str):
    print(f"  Parsing JSON...")
    try:
        return json.loads(json_str)
    except:
        return None

json_str = '{"name": "Alice", "age": 30}'

# Without walrus - parses twice
if parse_json(json_str):
    data = parse_json(json_str)
    print(f"Name: {data.get('name')}")

# With walrus - parses once
if (data := parse_json(json_str)):
    print(f"Name (walrus): {data.get('name')}")

# API calls
print("\nAPI calls:")

def fetch_data(endpoint):
    print(f"  Fetching from {endpoint}...")
    return {"status": 200, "data": [1, 2, 3, 4, 5]}

# Without walrus - fetches twice
endpoint = "/api/users"
if fetch_data(endpoint).get("status") == 200:
    response = fetch_data(endpoint)
    print(f"Data: {response.get('data')}")

# With walrus - fetches once
if (response := fetch_data(endpoint)).get("status") == 200:
    print(f"Data (walrus): {response.get('data')}")

# Validation chains
print("\nValidation chains:")

def validate_input(value):
    print(f"  Validating {value}...")
    return value if value and len(value) >= 3 else None

user_input = "alice"

# Without walrus - validates multiple times
if validate_input(user_input):
    validated = validate_input(user_input)
    if len(validated) <= 10:
        final = validated.upper()
        print(f"Valid: {final}")

# With walrus - validates once
if (validated := validate_input(user_input)) and len(validated) <= 10:
    final = validated.upper()
    print(f"Valid (walrus): {final}")

# Practical example
print("\nPractical example:")

# Cache lookup
cache = {1: "cached_value_1"}

def compute_expensive(key):
    print(f"  Computing for key {key}...")
    return f"computed_value_{key}"

def get_or_compute(key):
    # Without walrus
    if key in cache:
        return cache[key]
    else:
        result = compute_expensive(key)
        cache[key] = result
        return result

# With walrus - cleaner
def get_or_compute_walrus(key):
    if (result := cache.get(key)) is not None:
        return result
    cache[key] = (result := compute_expensive(key))
    return result

print(f"Get 1: {get_or_compute_walrus(1)}")  # From cache
print(f"Get 2: {get_or_compute_walrus(2)}")  # Computed

# Form validation
def validate_email(email):
    print(f"  Validating email: {email}")
    return "@" in email and "." in email

def validate_age(age):
    print(f"  Validating age: {age}")
    return 0 < age < 150

email = "alice@example.com"
age = 30

# Without walrus - validates each twice
if validate_email(email) and validate_age(age):
    email_valid = validate_email(email)
    age_valid = validate_age(age)
    print(f"Email: {email_valid}, Age: {age_valid}")

# With walrus - validates once each
if (email_valid := validate_email(email)) and (age_valid := validate_age(age)):
    print(f"Email (walrus): {email_valid}, Age: {age_valid}")

walrus_pitfalls.py
"""When not to use walrus operator"""

# Reduces readability
print("Reduces readability:")

# Bad - too complex
numbers = [1, 2, 3, 4, 5]
result = [y for x in numbers if (y := x * 2) > 5 if (z := y + 1) < 15]
print(f"Complex walrus: {result}")

# Better - split into steps
doubled = [x * 2 for x in numbers]
filtered = [y for y in doubled if y > 5 and y + 1 < 15]
print(f"Clear steps: {filtered}")

# Unnecessary complexity
print("\nUnnecessary complexity:")

# Bad - walrus adds no value
if (x := 5) == 5:
    print(f"x is {x}")

# Better - simple assignment
x = 5
if x == 5:
    print(f"x is {x}")

# Simple assignments
print("\nSimple assignments:")

# Bad - walrus not needed
data = [1, 2, 3]
if (length := len(data)) > 0:
    print(f"Has {length} items")

# Better - separate lines when not reusing
if len(data) > 0:
    print(f"Has {len(data)} items")

# Good use - value reused
if (length := len(data)) > 0:
    average = sum(data) / length
    print(f"Average of {length} items: {average}")

# Mutation confusion
print("\nMutation confusion:")

# Bad - unclear what's being assigned
items = [1, 2, 3]
if (items := items + [4]) and len(items) > 3:
    print(f"Items: {items}")

# Better - explicit mutation
items = [1, 2, 3]
items = items + [4]
if len(items) > 3:
    print(f"Items: {items}")

# Debugging difficulty
print("\nDebugging difficulty:")

# Bad - hard to debug
def process(x):
    return x * 2

numbers = [1, 2, 3, 4, 5]
if any((result := process(n)) > 5 for n in numbers):
    print(f"Found: {result}")  # Which result?

# Better - explicit loop
for n in numbers:
    result = process(n)
    if result > 5:
        print(f"Found: {result}")
        break

# Scope confusion
print("\nScope confusion:")

# Bad - walrus creates variable in outer scope
data = [1, 2, 3, 4, 5]
evens = [x for x in data if (is_even := x % 2 == 0)]
print(f"Last is_even value: {is_even}")  # Unexpected side effect

# Better - keep scope clear
evens = [x for x in data if x % 2 == 0]
print(f"Evens: {evens}")

# Multiple assignments
print("\nMultiple assignments:")

# Bad - trying to assign multiple values
try:
    if (a := 1, b := 2):  # Syntax error
        pass
except SyntaxError:
    print("Cannot use multiple := in one expression")

# Better - separate assignments
a, b = 1, 2
if a and b:
    print(f"a={a}, b={b}")

# Class attributes
print("\nClass attributes:")

# Bad - walrus in class definition
class BadExample:
    # This doesn't work as expected
    # value = (cached := compute())
    pass

# Better - regular assignment
class GoodExample:
    def __init__(self):
        self.value = self.compute()
    
    def compute(self):
        return 42

obj = GoodExample()
print(f"Value: {obj.value}")

# Performance misconception
print("\nPerformance misconception:")

# Bad - thinking walrus improves performance
numbers = list(range(50))
# Walrus doesn't make this faster
total = sum(n for n in numbers if (doubled := n * 2) > 500)

# Better - same speed, clearer
total = sum(n * 2 for n in numbers if n * 2 > 500)
print(f"Total: {total}")

# When walrus is good
print("\nWhen walrus is good:")

# Good: Avoid repeated expensive calls
def expensive():
    return 42

if (result := expensive()) > 40:
    print(f"Good use: {result}")

# Good: Capture match object
import re
text = "Error: Something failed"
if (match := re.search(r"Error: (.*)", text)):
    print(f"Good use: {match.group(1)}")

# Good: Read lines
lines = iter(["line 1", "line 2", ""])
while (line := next(lines, None)) and line:
    print(f"Good use: {line}")

# Best practices
print("\nBest practices:")

# DO use walrus when:
# 1. Avoiding repeated expensive calls
def fetch_data():
    return {"value": 100}

if (data := fetch_data()).get("value", 0) > 50:
    print(f"✓ Data: {data}")

# 2. Capturing intermediate values in comprehensions
numbers = [1, 2, 3, 4, 5]
squares = [sq for n in numbers if (sq := n * n) > 10]
print(f"✓ Squares: {squares}")

# DON'T use walrus when:
# 1. Simple assignments suffice
x = 10  # Not: if (x := 10)
print(f"✓ Simple: {x}")

# 2. Reduces readability
# Not: if (a := b := c := 1)
a = b = c = 1
print(f"✓ Clear: a={a}")

# 3. In complex nested expressions
# Keep it simple!

The walrus operator is a tool for specific situations. Overusing it makes code harder to read and debug.

Use Cases

  • Avoid repeated function calls in conditions
  • Capture values in comprehensions
  • Simplify file and stream reading loops
  • Debug intermediate values
  • Reduce temporary variable clutter
assignment expression Using `:=` to assign a value and use it in the same expression, eliminating redundant computations or awkward pre-assignment.
loop assignment Using `:=` in loop conditions to assign and test in one expression, creating cleaner read-until patterns.
comprehension capture Using `:=` inside list/dict comprehensions to avoid computing expensive expressions twice in the filter and output.
call elimination Using `:=` to call a function once and reuse its result, avoiding duplicate expensive operations like database queries or API calls.
walrus pitfalls Cases where the walrus operator reduces readability or adds unnecessary complexity, including simple assignments and nested expressions.

Exercise: walrus_practice.py

Refactor a function that calls len() twice (once for check, once for use) to use the walrus operator