Skip to content

Chained Assignment

Basic Chaining

1. Simple Case

a = b = c = 1
print(f"{a = }, {b = }, {c = }")
# a = 1, b = 1, c = 1

2. Single Object

All names refer to same object:

a = b = c = [1, 2, 3]
print(f"{a is b is c = }")  # True
print(f"{id(a) == id(b) == id(c) = }")  # True

Evaluation Order

1. Right-to-Left

Expression evaluated right-to-left:

a = b = c = 1 + 2 + 3
# 1. Compute: 1 + 2 + 3 = 6
# 2. Create object: 6
# 3. Assign: c, then b, then a

2. Bytecode Evidence

import dis

def f():
    a = b = c = 1

dis.dis(f)

Shows: - LOAD_CONST 1 - DUP_TOP (duplicate) - DUP_TOP (duplicate) - STORE_NAME (c, b, a)

Shared Mutability

1. Lists

x = y = z = [1, 2, 3]

# Modify through one
x.append(4)

# All see change
print(f"{x = }")  # [1, 2, 3, 4]
print(f"{y = }")  # [1, 2, 3, 4]
print(f"{z = }")  # [1, 2, 3, 4]

2. Dictionaries

a = b = c = {'key': 'value'}

a['new'] = 'data'

print(b)  # {'key': 'value', 'new': 'data'}
print(c)  # {'key': 'value', 'new': 'data'}

Immutable Objects

1. No Issue

x = y = z = 42

x = 100  # Rebinds x only

print(f"{x = }")  # x = 100
print(f"{y = }")  # y = 42
print(f"{z = }")  # z = 42

2. String Example

a = b = c = "hello"

a = "world"  # Rebinds a

print(f"{a = }")  # a = 'world'
print(f"{b = }")  # b = 'hello'
print(f"{c = }")  # c = 'hello'

Best Practices

1. Use for Immutables

# Safe: immutable
MAX = DEFAULT = LIMIT = 100

2. Avoid for Mutables

# Dangerous
x = y = z = []  # Same list!

# Better
x = []
y = []
z = []  # Three lists

3. Default Arguments

# Don't do this!
def bad(element, target=x=y=[]):
    target.append(element)
    return target

# Better
def good(element, target=None):
    if target is None:
        target = []
    target.append(element)
    return target

Identity Checks

1. Verify Sharing

a = b = c = [1, 2, 3]

print(f"a is b: {a is b}")
print(f"b is c: {b is c}")
print(f"a is c: {a is c}")
# All True

print(f"IDs: {id(a)}, {id(b)}, {id(c)}")
# All same

2. Break Sharing

a = b = c = [1, 2, 3]

# Rebind one name
a = [1, 2, 3]  # New object

print(f"a is b: {a is b}")  # False
print(f"b is c: {b is c}")  # True

Use Cases

1. Constants

# Good use case
ZERO = NULL = EMPTY = 0

2. Counters

# Acceptable
wins = losses = draws = 0

wins += 1  # Only affects wins

3. Coordinates

# OK for numbers
x = y = z = 0.0

# Dangerous for lists
# positions = velocities = [0, 0, 0]