Adding Elements to a Python List: append, extend, insert
python list add element: Learn how to add an element to a Python list with append, extend, insert, and the + operator, including runtime costs and common mistakes.
python list add element requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
When you need to add an element to a Python list, the standard library offers three dedicated methods — append, extend, and insert — along with the + and += operators. Each one differs in what it adds, where it places the new value, and what it costs at runtime. Picking the wrong method rarely raises an error; it usually produces a subtly wrong data structure, which makes the choice worth understanding precisely.
append() Adds a Single Element to the End
list.append(x) takes exactly one value and places it at the end of the list. The list grows by exactly one slot.
tasks = ["parse", "validate"] tasks.append("transform") print(tasks) # ['parse', 'validate', 'transform']
The argument is passed as a single object. If you pass a list, that list becomes one nested element rather than being unpacked. This is the most common source of confusion with append, and it is covered in more detail later. For a single scalar value, append is the clearest and most direct way to add to the end of a list.
extend() Adds Every Element From an Iterable
list.extend(iterable) iterates over the argument and appends each item individually. The list grows by the number of items in the iterable.
numbers = [1, 2, 3] numbers.extend([4, 5]) print(numbers) # [1, 2, 3, 4, 5]
extend accepts any iterable — a list, tuple, set, generator, or string. The elements are copied into the list one by one. This is the correct method when you want to merge another collection into the list rather than nesting it.
The contrast with append is the key distinction:
numbers = [1, 2, 3] numbers.append([4, 5]) print(numbers) # [1, 2, 3, [4, 5]]
append treats [4, 5] as one object; extend treats it as two. If the goal is a flat list of integers, extend is the right choice.
insert() Places an Element at a Specific Index
list.insert(index, x) inserts x before the position given by index. All elements from that position onward shift one slot to the right.
queue = ["first", "third"] queue.insert(1, "second") print(queue) # ['first', 'second', 'third']
The index follows the same rules as slicing. A negative index counts from the end, so insert(-1, x) places the value before the last element, not at the very end. If the index is greater than or equal to the list length, the value is appended at the end; if it is less than the negative length, it is inserted at the front.
items = [1, 2, 3] items.insert(-1, 99) print(items) # [1, 99, 2, 3]
Because every element after the insertion point must be shifted, insert runs in O(n) time. Inserting near the front of a large list repeatedly becomes expensive quickly.
Concatenation With + and += Creates or Extends a List
The + operator builds a brand new list containing the elements of both operands. The original lists are not modified.
left = [1, 2] right = [3, 4] combined = left + right print(combined) # [1, 2, 3, 4] print(left) # [1, 2]
The += operator behaves differently: it extends the list in place, equivalent to calling extend with the right-hand operand. The original list object is reused, which matters when other references point to the same list.
left = [1, 2] right = [3, 4] left += right print(left) # [1, 2, 3, 4]
+ requires both operands to be lists and allocates a new list, so it costs O(n + m) time and memory. += accepts any iterable on the right and avoids the extra allocation by reusing the existing list's capacity.
Performance and Memory Behavior
The runtime cost of each approach depends on how much of the list has to move. The table below summarizes the complexity for each operation.
| Operation | Adds | Position | Time complexity |
|---|---|---|---|
append(x) | one element | end | O(1) amortized |
extend(iterable) | all items | end | O(k) |
insert(i, x) | one element | index i | O(n) |
a + b | all items | end (new list) | O(n + m) |
a += b | all items | end (in place) | O(k) |
append is amortized O(1) because the list occasionally reallocates its underlying buffer when capacity runs out, but the average cost per append stays constant. extend is O(k) where k is the number of items added. insert is O(n) because shifting elements dominates the cost. Concatenation with + allocates a new list, so it uses extra memory proportional to the combined size.
For building a list incrementally in a loop, append is the standard choice. For merging two existing collections, extend or += avoids the extra allocation of +. For inserting at the front of a list repeatedly, none of these methods is efficient; that scenario calls for a different data structure.
Common Mistakes When Adding Elements
The most frequent error is using append where extend is intended. Passing a list to append nests it instead of flattening it, and the bug often surfaces only when the code later iterates over the list and encounters an unexpected inner collection.
Another common failure is modifying a list while iterating over it. Adding elements inside a for loop over the same list can cause the loop to visit the newly added items, which usually leads to an infinite loop or an incomplete result.
numbers = [1, 2, 3] for n in numbers: if n < 3: numbers.append(n * 10) # The loop keeps finding new elements and never terminates.
A safer pattern is to collect the new values in a separate list and extend the original afterward, or to iterate over a copy of the list.
A third mistake is assuming insert with an index beyond the current length raises an error. It does not; the value is simply appended. Code that relies on insert to enforce ordering should validate the index explicitly when the position matters.
When a List Is Not the Right Choice
If the dominant operation is adding elements at the front, a list is the wrong structure. insert(0, x) shifts the entire list on every call, making a loop that prepends n items run in O(n²) time. collections.deque supports appendleft in O(1) time and is the appropriate replacement when the front of the sequence is the hot path.
If the sequence never changes after construction, a tuple is more appropriate than a list. Tuples are immutable, so they cannot be modified accidentally, and they can be used as dictionary keys or set members where lists cannot. Converting a list to a tuple after building it is a common pattern when the data is final.
The choice between these structures should be driven by what the code does most often: appending at the end favors a list with append; prepending favors deque; fixed data favors a tuple.