Multiple assignment, often referred to as tuple unpacking or iterable unpacking, is a powerful and idiomatic feature of Python that allows you to assign values from an iterable (like a tuple, list, or string) to multiple variables in a single, concise statement. This mechanism is deeply rooted in Python’s object model and its concept of iterability.

The Basic Syntax and Mechanics

At its simplest, multiple assignment involves a tuple (or list) of variables on the left-hand side of the assignment operator (=) and an iterable of the same length on the right-hand side. Python evaluates the expression on the right, creates an iterable object, and then assigns each element, in order, to the corresponding variable on the left.

# Assigning values from a tuple literal
name, age, occupation = ("Alice", 30, "Engineer")
print(name)       # Output: Alice
print(age)        # Output: 30
print(occupation) # Output: Engineer

# The parentheses are often omitted, as the comma creates a tuple.
fruit, count, price = "apple", 5, 1.25
print(fruit) # Output: apple

# The right-hand side can be any iterable, like a list.
[x, y, z] = [10, 20, 30]
print(y) # Output: 20

# It works with strings (which are iterables of characters).
a, b, c = "XYZ"
print(b) # Output: Y

The process is efficient because it leverages Python’s iterator protocol. The statement a, b = iterable is functionally equivalent to:

temp_iterator = iter(iterable)
a = next(temp_iterator)
b = next(temp_iterator)

Swapping Variables Without a Temporary Variable

One of the most celebrated uses of multiple assignment is elegantly swapping the values of two variables. In many other languages, this requires a third, temporary variable. Python’s tuple unpacking handles this seamlessly by first evaluating the entire right-hand side, packing the current values into a temporary tuple, and then unpacking them to the left-hand side.

a = 5
b = 10

# The classic, multi-line approach in other languages
# temp = a
# a = b
# b = temp

# The Pythonic one-liner
a, b = b, a

print(a) # Output: 10
print(b) # Output: 5

This works because the expression b, a on the right-hand side is evaluated first, creating the tuple (10, 5). Then the assignment happens, unpacking the first element (10) to a and the second element (5) to b.

Using the Starred Expression (*) for Extended Unpacking

A common pitfall occurs when the number of elements in the iterable doesn’t match the number of variables. Python will raise a ValueError. To handle this, Python provides the starred expression (*), which allows a variable to “catch” a variable number of elements.

# This will raise: ValueError: too many values to unpack (expected 3)
# first, second, third = [1, 2, 3, 4, 5]

# Using a starred expression to capture the "rest"
first, second, *remaining = [1, 2, 3, 4, 5]
print(first)     # Output: 1
print(second)    # Output: 2
print(remaining) # Output: [3, 4, 5] (a list)

# The starred variable can be in any position
first, *middle, last = [1, 2, 3, 4, 5]
print(middle) # Output: [2, 3, 4]

*beginning, penultimate, last = [1, 2, 3, 4, 5]
print(beginning) # Output: [1, 2, 3]

The starred variable will always be assigned a list, even if it captures zero elements. It provides a clean and readable way to handle iterables of unknown or variable length.

Ignoring Unneeded Values with Underscore (_)

Often when unpacking, you may only be interested in a few specific values from the iterable (e.g., only the first and last items). While you could use a starred expression, a common best practice is to use the underscore (_) as a placeholder for ignored values. This is a convention that improves code readability by signaling intent—that the value is being deliberately discarded.

# Unpacking a tuple from a function where we only need the first value
data = (42, "unused string", [1,2,3])
important_value, _, _ = data
print(important_value) # Output: 42

# Ignoring multiple values in the middle
first, *_, last = [10, 20, 30, 40, 50, 60, 70]
print(first) # Output: 10
print(last)  # Output: 70
print(_)     # Output: [20, 30, 40, 50, 60] (but you shouldn't use _ after this)

It’s important to note that _ is a valid variable name and can be used later, but by convention, it should not be. Using it for ignored values is a clear signal to other programmers.

Deep Dive: How It Works with the Object Model

Multiple assignment is a perfect illustration of Python’s “everything is an object” model. The statement a, b = c, d does not magically link variables; it follows a clear sequence of operations:

  1. Evaluation: The right-hand side expression (c, d) is evaluated. If c and d are variables, their object references are retrieved. A new tuple object is created containing these two references.
  2. Unpacking: The left-hand side (a, b) signals that an unpacking assignment should occur. Python gets an iterator for the new tuple.
  3. Assignment: The next() function is called on the iterator, and the returned object reference is assigned to a. This process repeats for b. This means that if the right-hand side contains mutable objects, the assigned variables become new references to those same objects, not copies.
list_a = [1, 2]
list_b = [3, 4]
container = (list_a, list_b) # tuple holds references to the two list objects

x, y = container # x and y now hold those same references

x.append(99) # Modifying the list object through reference x
print(list_a) # Output: [1, 2, 99] (the original object is changed)

Common Pitfalls and Best Practices

  • Mismatched Lengths: The most frequent error is an unpacking length mismatch. Always ensure the number of non-starred variables matches the number of elements to be assigned, or use a starred expression to handle the discrepancy.
  • Using _ Conventionally: Use _ for values you intend to ignore. Do not use the variable _ later in the code, as its purpose is to signal it’s a throwaway value. Some Python REPLs automatically assign the result of the last operation to _, so reusing it can cause confusion.
  • Clarity over Cleverness: While a, b = b, a is idiomatic and encouraged, avoid overly complex unpacking in a single line that harms readability. Breaking a complicated unpacking operation into multiple lines is often better.
  • Unpacking in For Loops: This is an extremely common and powerful pattern for iterating over sequences of sequences (e.g., a list of tuples).
    people = [("Alice", 30), ("Bob", 25), ("Charlie", 35)]
    for name, age in people: # Unpacks each tuple into name and age
        print(f"{name} is {age} years old.")