Back to Blog
Python

Python List Extend: Syntax, Usage, and When to Use It

Learn how Python's list.extend() adds multiple elements to an existing list, with syntax, examples, and comparisons to append() and +.

listextenditerablessequence operationsPython
A visual representation of a Python list being extended with new elements from an iterable, shown as a chain of connected blocks.

The list.extend() method in Python appends all items from an iterable to the end of a list in place. It mutates the original list and returns None. This differs from +, which creates a new list, and from append(), which adds one object as a single element. Knowing these differences helps you choose the right way to merge collections.

The extend() Method Syntax and Behavior

The syntax is straightforward: list.extend(iterable). The method takes any iterable—a list, tuple, string, set, dictionary, or generator—and adds each element from that iterable to the end of the list. The operation is performed in place, so the original list is changed and no new list is returned.

numbers = [1, 2, 3] numbers.extend([4, 5, 6]) print(numbers) # [1, 2, 3, 4, 5, 6]

Because extend() returns None, it is not chainable. Code such as new_list = old_list.extend([1, 2]) sets new_list to None, and calling another method on the return value, for example old_list.extend([1, 2]).sort(), raises an AttributeError. The method works on any list, including empty lists, and the iterable does not need to be a list.

Extending with Different Iterable Types

extend() accepts any iterable. This includes tuples, sets, strings, and even generators. The behavior depends on how the iterable yields its elements.

# Tuple fruits = ['apple'] fruits.extend(('banana', 'cherry')) print(fruits) # ['apple', 'banana', 'cherry'] # String tags = ['python'] tags.extend('code') print(tags) # ['python', 'c', 'o', 'd', 'e']

When you extend with a string, the string is treated as an iterable of characters, so each character becomes a separate list element. If you want to add the whole string as one element, use append() instead.

Dictionaries are iterable over their keys, so extending with a dictionary adds its keys, not the key-value pairs. Sets are unordered, so the order of added elements is not guaranteed. Generators are also valid; extend() consumes the generator and adds each yielded value.

def gen(): yield 10 yield 20 values = [] values.extend(gen()) print(values) # [10, 20]

This flexibility makes extend() a versatile tool for combining data from different sources.

Comparing extend() with append() and the + Operator

A common source of confusion is the difference between extend(), append(), and the + operator. Each serves a distinct purpose.

  • append(x) adds x as a single element, even if x is a list. It does not iterate over the argument.
  • extend(iterable) adds each element of the iterable individually.
  • list1 + list2 creates a new list containing the elements of both lists, leaving the originals unchanged.
a = [1, 2] b = [3, 4] a.append(b) print(a) # [1, 2, [3, 4]] a = [1, 2] a.extend(b) print(a) # [1, 2, 3, 4] a = [1, 2] c = a + b print(c) # [1, 2, 3, 4] print(a, b) # [1, 2] [3, 4]

When you need to mutate an existing list, extend() is the direct choice. The + operator is better when you want to create a new list without side effects. append() is for adding a single object, such as a nested list or a tuple that should remain intact.

Common Mistakes and Edge Cases

One frequent mistake is using extend() with a string when you intended to add the string as a single item. Another is expecting extend() to return a new list. Since it returns None, new_list = old_list.extend([1, 2]) sets new_list to None.

Edge cases also arise with empty iterables. Calling extend([]) or extend(()) leaves the list unchanged. This is harmless but can be a sign of a logic error if you expected at least one element.

When extending with a generator that raises an exception, the list is partially modified before the exception propagates. For example:

def broken_gen(): yield 1 raise ValueError('stop') nums = [0] try: nums.extend(broken_gen()) except ValueError: pass print(nums) # [0, 1]

The list now contains 1 even though the operation failed. This partial mutation can be surprising. If you need atomicity, build a complete temporary list first and then extend() the original list with that temporary list.

Performance and Memory Considerations

In CPython, extend() is implemented in C and is generally faster than repeatedly calling append() in a loop, especially for large iterables. The time complexity is O(k), where k is the number of elements added. The actual cost can depend on the iterable type: for a list argument, CPython can use an optimized path that copies elements directly rather than going through Python-level iteration.

Memory usage is also worth considering. Since extend() mutates the list in place, Python may reallocate the underlying array when the current capacity is insufficient. This reallocation is amortized, so the average cost per element is constant. If you know the final size in advance and are filling positions by index, items = [None] * n can avoid repeated growth. For normal extend() use, however, the built-in growth strategy is usually sufficient.

One caveat: when extending with a generator, the elements are produced one by one, so the operation cannot be optimized by copying a contiguous block. This is fine for small generators. For very large data, you might prefer to collect the generator output into a list first if you need to reuse it.

When to Use extend() vs Other Approaches

The choice between extend(), append(), and + depends on your specific goal.

Use extend() when you have an existing list and want to add multiple elements from any iterable, and you are comfortable mutating the original list. This is common in data processing pipelines where you accumulate results.

Use append() when you need to add a single object, especially if that object is itself a collection that should be treated as one item. For example, adding a tuple representing a coordinate to a list of coordinates.

Use + when you want to combine two lists without modifying either input. This is useful in functional-style code where immutability is preferred. Note that + only works with two lists, not arbitrary iterables. To concatenate a list with a tuple, you would need to convert the tuple to a list first.

For repeated concatenation in a loop, avoid + because it creates a new list each time, leading to O(n^2) behavior. Instead, build a list with extend() or use itertools.chain and then convert to a list once.

# Inefficient total = [] for sublist in list_of_lists: total = total + sublist # Efficient total = [] for sublist in list_of_lists: total.extend(sublist)

The second version reuses the same list and avoids repeated allocation. This is a common performance optimization in real-world code.

Using extend() with a Generator Expression

A practical pattern is to use extend() with a generator expression to filter and transform data before adding it. This keeps the logic concise and avoids an intermediate list. A list comprehension can also be passed to extend(), but it materializes a temporary list first.

numbers = [1, 2, 3, 4, 5] even_squares = [] even_squares.extend(x * x for x in numbers if x % 2 == 0) print(even_squares) # [4, 16]

Here, extend() consumes the generator expression and adds each computed value. This is memory-efficient because the generator does not create a full list of squares; it yields them one at a time. If you need the squares for later use, you might prefer a list comprehension, but for immediate consumption, the generator is fine.

Compatibility and Version Notes

extend() is part of the standard list API in Python 3. The method works with any iterable object. In Python 3, range objects are iterable, so you can extend a list with a range directly:

a = [] a.extend(range(5)) print(a) # [0, 1, 2, 3, 4]

This is a common idiom for initializing a list with a sequence of integers. In older Python 2 code, xrange was the memory-efficient equivalent, but current Python code should use range.

One subtle point: extend() does not accept a single non-iterable argument. Passing an integer raises a TypeError: 'int' object is not iterable. This is a frequent error for beginners. Always ensure the argument is iterable.

The Role of extend() in Object-Oriented Design

In custom classes that mimic sequences, you might implement an extend() method to match the built-in list API. This is useful when you want your class to behave like a list for users. For example, a custom collection class could have an extend() that delegates to an internal list. This maintains consistency and allows code that expects a list-like interface to work with your class.

class MyCollection: def __init__(self): self._items = [] def extend(self, iterable): self._items.extend(iterable) def __iter__(self): return iter(self._items)

By providing an extend() method, you allow callers to use the same idiom they would with a list. This is part of Python's duck-typing philosophy. However, be careful to match the semantics: extend() should mutate the object in place and return None. If you need to return a new object, use a different method name to avoid confusion.

Final Code Example: Merging Lists Without Duplicates

A realistic use case is merging several lists without duplicates. Suppose you have a list of lists and want to collect all elements that appear in at least one sublist, skipping duplicates. You can use a set to track seen items.

def merge_unique(lists): result = [] seen = set() for sublist in lists: for item in sublist: if item not in seen: seen.add(item) result.append(item) return result # Alternative using extend and a generator expression def merge_unique_extend(lists): result = [] seen = set() for sublist in lists: result.extend( item for item in sublist if not (item in seen or seen.add(item)) ) return result

The second version uses a generator expression with a side effect in the condition, which is clever but harder to read. In production code, the explicit loop is often clearer. The point is that extend() can be combined with generator expressions to build lists efficiently, but readability should guide your choice.

When you need to add many elements to an existing list, extend() is the idiomatic and efficient way. It handles any iterable, mutates in place, and avoids the overhead of repeated append() calls. Understanding its behavior, especially the differences from append() and +, helps you write correct and performant Python code.

Python List Extend: Practical Usage and Code Examples | RYUSLOG DEV