How can you insert an element at a given index in a given list? Python’s insert()
method is your friend.
This tutorial shows you everything you need to know to help you master an essential method of the most fundamental container data type in the Python programming language.
Definition and Usage:
The list.insert(i, element)
method adds an element element
to an existing list
at position i
. All elements j>i
will be moved by one index position to the right.
Here’s a short example:
>>> lst = [1, 2, 3, 4] >>> lst.insert(3, 99) >>> lst [1, 2, 3, 99, 4]
In the first line of the example, you create the list lst
. You then insert the integer element 99 at position 3 of the list. The result is the list with five elements [1, 2, 3, 99, 4]
.
Syntax:
You can call this method on each list object in Python. Here’s the syntax:
list.insert(index, element)
Arguments:
Argument | Description |
---|---|
index | Integer value representing the position before you want to insert an element |
element | Object you want to insert into the list. |
Video:
Code Puzzle:
Now you know the basics. Let’s deepen your understanding with a short code puzzle—can you solve it?
# Puzzle # Puzzle created by Finxter Lee lst = [1, 3, 4, 5, 6, 7, 8] lst.insert(1, 2) print(lst) # What's the output of this code snippet?
You can check out the solution on the Finxter app.
Examples:
To help you fully understand the ins and outs of the insert()
method, let’s dive into a few more examples.
>>> lst = [1, 2, 3] >>> lst.insert(0, 99) >>> lst [99, 1, 2, 3] >>> lst.insert(-1, 42) >>> lst [99, 1, 2, 42, 3] >>> lst.insert(100, 0) >>> lst [99, 1, 2, 42, 3, 0]
An interesting case is the second one where you use a negative index in the insert()
method:
Related articles:
Here’s your free PDF cheat sheet showing you all Python list methods on one simple page. Click the image to download the high-resolution PDF file, print it, and post it to your office wall:
Python List insert() Negative Index
You can use a negative index in the lst.insert(index, element)
method. With a negative index you count backwards, starting from the right. In other words, the index -1
stands for the rightmost element in the list. The insert()
method inserts the element right in front of the index position. Thus, you get the following behavior where the element is inserted to the second last position:
>>> lst = ["Ann", "Bob", "Alice"] >>> lst.insert(-1, "Liz") >>> lst ['Ann', 'Bob', 'Liz', 'Alice']
What happens if you count further, i.e., -2, -3, or even -99? Let’s check:
>>> lst.insert(-2, "Sam") >>> lst ['Ann', 'Bob', 'Sam', 'Liz', 'Alice']
… and …
>>> lst.insert(-3, "Bob") >>> lst ['Ann', 'Bob', 'Bob', 'Sam', 'Liz', 'Alice']
… and …
>>> lst.insert(-99, "Hans") >>> lst ['Hans', 'Ann', 'Bob', 'Bob', 'Sam', 'Liz', 'Alice']
So you can see that every integer index is allowed! If you overshoot (e.g. -99), it will simply insert at the beginning of the list.
Python List insert() At Beginning
The insert()
method allows you to add an element at the beginning of a list. Simply use the index 0 in the call lst.insert(0, element)
to add element to the beginning of the lst
.
Here’s an example:
>>> lst = [1, 2, 3] >>> lst.insert(0, 99) >>> lst [99, 1, 2, 3]
The insert(i, x)
method inserts an element x
at position i
in the list. This way, you can insert an element to each position in the list—even at the first position. Note that if you insert an element at the first position, each subsequent element will be moved by one position. In other words, element i
will move to position i+1
.
Python List insert() At End
Okay, you can insert an element at every position in the list—just use the first argument to define the insertion index. Consequently, you can “insert” an element at the end of a list by defining an index that’s greater or equal the size of the list. Remember, insert(index, element)
inserts element
before the element that’s currently at position index
. So if you define an index that’s at least the size of the list, you’ll insert the element at the end of the list:
>>> lst = [1, 2, 3, 4, 5, 6] >>> lst.insert(6, 99) >>> lst [1, 2, 3, 4, 5, 6, 99] >>> lst.insert(100000, 42) >>> lst [1, 2, 3, 4, 5, 6, 99, 42]
No matter your concrete choice of the index argument, as long as it’s larger or equal the current size of the list, your element will be inserted at the end of the list.
However, this doesn’t make a lot of sense considering that you can use the append(element)
method which adds an element at the end of the list—and is highly efficient (and readable). Read more about the append()
method at my detailed blog article.
Here’s an example adding the same elements 99 and 42 to the end of the list:
>>> lst = [1, 2, 3, 4, 5, 6] >>> lst.append(99) >>> lst.append(42) >>> lst [1, 2, 3, 4, 5, 6, 99, 42]
In Python, there are usually a lot of ways to accomplish the same thing. Using the append()
method to add an element to the end of the list is the better way!
Python insert() into Sorted List
How to insert an element into a sorted list (ascending order) and keep the list sorted?
The naive approach is to go over all elements, from first to last, as long as the element to be inserted is still smaller than the current element in the list. As soon as it’s larger, you go back one position and insert it there.
However, the computational complexity of the naive sorted insert algorithm is bad because you need up to n operations to insert an element into a list of n elements.
If you’re interested in the right way of doing it, you can use binary search and the list.insert(i,x)
method to insert element x
at position i
in the list. Hereβs the code for the binary search algorithm in Python:
def binary_search(lst, value): lo, hi = 0, len(lst)-1 while lo <= hi: mid = (lo + hi) // 2 if lst[mid] < value: lo = mid + 1 elif value < lst[mid]: hi = mid - 1 else: return mid return -1 l = [3, 6, 14, 16, 33, 55, 56, 89] x = 56 print(binary_search(l,x)) # 6 (the index of the found element)
Don’t worry if the code looks a bit complicated!
Python List insert() Multiple Elements
How can you insert multiple elements into a list? Just call the insert()
method multiple times—once for each element to be inserted! But be careful: you must reverse the order of the elements to be inserted first if you keep the insert position constant. Here’s what I mean:
>>> lst = [1, 2, 3, 4] >>> insert = [42, 99] >>> insert.reverse() >>> for el in insert: lst.insert(2, el) >>> lst [1, 2, 42, 99, 3, 4]
You want to insert elements 42 and 99 at position 2 of the list lst
, in that order. If you’d just iterate over both elements, you’d first insert element 42 before index 2. Now, element 42 obtains the position 2 in the list. Next, you’d insert element 99 before position 2. Now, element 99 obtains position 2 in the list. So basically, you’ve reversed the original order of the elements to be inserted. Therefore, you have to call the reverse()
method first before inserting the elements into the list.
There’s really no better alternative. Of course, you could use list concatenation but that is not as efficient because you’d create a new list rather than modify an existing list:
>>> lst = [1, 2, 3, 4] >>> insert = [42, 99] >>> lst = lst[:2] + insert + lst[2:] >>> lst [1, 2, 42, 99, 3, 4]
This may be more readable but it’s not as efficient because list concatenation creates a new list each time it’s used. If you need to refresh your basic Python slicing skills (e.g. in the operation lst[:2]
), check out this detailed blog tutorial.
Python List insert() Complexity
Time Complexity: The insert()
method has constant time complexity O(1) no matter the number of elements in the list. The reason is that Python lists are implemented with variable-length array lists so accessing a certain position is fast. Also inserting an element into the array list is fast because you only have to modify the pointers of the preceding and following elements in the list.
Python List insert() vs append()
The difference between the append()
and the insert()
method is the following:
- the
append(x)
method adds new elementx
to the end of the list, and - the
insert(i, x)
method adds new elementx
at positioni
in the list. It shifts all subsequent elements one position to the right.
Here’s an example showing both append()
and insert()
methods in action:
>>> l = [1, 2, 3] >>> l.append(99) >>> l [1, 2, 3, 99] >>> l.insert(2, 42) >>> l [1, 2, 42, 3, 99]
Both methods help you add new elements to the list. But you may ask:
Which is faster, append() or insert()?
All things being equal, the append()
method is significantly faster than the insert()
method.
Here’s a small script that shows that the append()
method has a huge performance advantage over the insert()
method when creating a list with 100,000 elements.
import time l1 = [] l2 = [] t1 = time.time() for i in range(100000): l1.append(i) t2 = time.time() for i in range(100000): l2.insert(0,i) t3 = time.time() print("append(): " + str(t2 - t1) + " seconds") print("insert(): " + str(t3 - t2) + " seconds") # OUTPUT: # append(): 0.015607357025146484 seconds # insert(): 1.5420396327972412 seconds
The experiments were performed on my notebook with an Intel(R) Core(TM) i7-8565U 1.8GHz processor (with Turbo Boost up to 4.6 GHz) and 8 GB of RAM.
Alternatives: Python List append() vs extend()
Instead of inserting an element, you can also add a single element to the end of the list using the append()
method or even adding multiple elements to the end of the list using the extend()
method.
What’s the difference?
I shot a small video explaining the difference and which method is faster, too:
The method list.append(x)
adds element x
to the end of the list
.
The method list.extend(iter)
adds all elements in iter
to the end of the list
.
The difference between append()
and extend()
is that the former adds only one element and the latter adds a collection of elements to the list.
You can see this in the following example:
>>> l = [] >>> l.append(1) >>> l.append(2) >>> l [1, 2] >>> l.extend([3, 4, 5]) >>> l [1, 2, 3, 4, 5]
In the code, you first add integer elements 1 and 2 to the list using two calls to the append()
method. Then, you use the extend method to add the three elements 3, 4, and 5 in a single call of the extend()
method.
Which method is faster — extend() vs append()?
To answer this question, I’ve written a short script that tests the runtime performance of creating large lists of increasing sizes using the extend()
and the append()
methods.
Our thesis is that the extend()
method should be faster for larger list sizes because Python can append elements to a list in a batch rather than by calling the same method again and again.
I used my notebook with an Intel(R) Core(TM) i7-8565U 1.8GHz processor (with Turbo Boost up to 4.6 GHz) and 8 GB of RAM.
Then, I created 100 lists with both methods, extend()
and append()
, with sizes ranging from 10,000 elements to 1,000,000 elements. As elements, I simply incremented integer numbers by one starting from 0.
Here’s the code I used to measure and plot the results: which method is faster—append()
or extend()
?
import time def list_by_append(n): '''Creates a list & appends n elements''' lst = [] for i in range(n): lst.append(n) return lst def list_by_extend(n): '''Creates a list & extends it with n elements''' lst = [] lst.extend(range(n)) return lst # Compare runtime of both methods list_sizes = [i * 10000 for i in range(100)] append_runtimes = [] extend_runtimes = [] for size in list_sizes: # Get time stamps time_0 = time.time() list_by_append(size) time_1 = time.time() list_by_extend(size) time_2 = time.time() # Calculate runtimes append_runtimes.append((size, time_1 - time_0)) extend_runtimes.append((size, time_2 - time_1)) # Plot everything import matplotlib.pyplot as plt import numpy as np append_runtimes = np.array(append_runtimes) extend_runtimes = np.array(extend_runtimes) print(append_runtimes) print(extend_runtimes) plt.plot(append_runtimes[:,0], append_runtimes[:,1], label='append()') plt.plot(extend_runtimes[:,0], extend_runtimes[:,1], label='extend()') plt.xlabel('list size') plt.ylabel('runtime (seconds)') plt.legend() plt.savefig('append_vs_extend.jpg') plt.show()
The code consists of three high-level parts:
- In the first part of the code, you define two functions
list_by_append(n)
andlist_by_extend(n)
that take as input argument an integer list sizen
and create lists of successively increasing integer elements using theappend()
andextend()
methods, respectively. - In the second part of the code, you compare the runtime of both functions using 100 different values for the list size
n
. - In the third part of the code, you plot everything using the Python matplotlib library.
Here’s the resulting plot that compares the runtime of the two methods append() vs extend(). On the x axis, you can see the list size from 0 to 1,000,000 elements. On the y axis, you can see the runtime in seconds needed to execute the respective functions.
The resulting plot shows that both methods are extremely fast for a few tens of thousands of elements. In fact, they are so fast that the time()
function of the time module cannot capture the elapsed time.
But as you increase the size of the lists to hundreds of thousands of elements, the extend()
method starts to win:
For large lists with one million elements, the runtime of the extend()
method is 60% faster than the runtime of the append()
method.
The reason is the already mentioned batching of individual append operations.
However, the effect only plays out for very large lists. For small lists, you can choose either method. Well, for clarity of your code, it would still make sense to prefer extend()
over append()
if you need to add a bunch of elements rather than only a single element.
Python List insert() Returns None
The return value of the insert()
method is None
. The return value of the insert()
method is not a modified list with the added elements. Assuming this is a common source of mistakes.
Here’s such an error where the coder wrongly assumed this:
>>> lst = [1, 2].insert(1, 99) >>> lst[0] Traceback (most recent call last): File "<pyshell#16>", line 1, in <module> lst[0] TypeError: 'NoneType' object is not subscriptable
It doesn’t make sense to assign the result of the insert()
method to another variable—because it’s always None
. Instead, the insert()
method changes a list object without creating (and returning) a new list.
Here’s the correct version of the same code:
>>> lst = [1, 2] >>> lst.insert(1, 99) >>> lst[1] 99
Now, you change the list object itself by calling the insert()
method on it. You through away the None
return value because it’s not needed.
Python List insert() Return New List
If you use the lst.insert(index, element)
operation, you add the element
to the existing list lst
. But what if you want to create a new list where all elements were added?
The answer is simply to use the list concatenation operation lst[:index] + [element] + lst[index:]
which creates a new list each time it is used. The original list lst
will not be affected by the list concatenation operation.
Here’s an example that shows that the insert()
method only modifies an existing list:
>>> lst_1 = [1, 2, 3] >>> lst_2 = lst_1.insert(1, 42) >>> lst_1 [1, 42, 2, 3] >>> lst_2 None
And here’s the example that shows how to create a new list inserting element 42 at index 2:
>>> lst_1 = [1, 2, 3] >>> lst_2 = lst_1[:1] + [42] + lst_1[1:] >>> lst_1 [1, 2, 3] >>> lst_2 [1, 42, 2, 3]
By using the list concatenation operation, you can create a new list rather than inserting the element into an existing list.
Python List insert() Thread Safe
Do you have a multiple threads that access your list at the same time? Then you need to be sure that the list operations (such as insert()
) are actually thread safe.
In other words: can you call the insert()
operation in two threads on the same list at the same time? (And can you be sure that the result is meaningful?)
The answer is yes (if you use the cPython implementation). The reason is Python’s global interpreter lock that ensures that a thread that’s currently working on it’s code will first finish its current basic Python operation as defined by the cPython implementation. Only if it terminates with this operation will the next thread be able to access the computational resource. This is ensured with a sophisticated locking scheme by the cPython implementation.
The only thing you need to know is that each basic operation in the cPython implementation is atomic. It’s executed wholly and at once before any other thread has the chance to run on the same virtual engine. Therefore, there are no race conditions. An example for such a race condition would be the following: the first thread reads a value from the list, the second threads overwrites the value, and the first thread overwrites the value again invalidating the second thread’s operation.
All cPython operations are thread-safe. But if you combine those operations into higher-level functions, those are not generally thread safe as they consist of many (possibly interleaving) operations.
Where to Go From Here?
The list.insert(index, element)
method inserts an element
to the end of the list
before the given index
position.
You’ve learned the ins and outs of this important Python list method.
If you keep struggling with those basic Python commands and you feel stuck in your learning progress, I’ve got something for you: Python One-Liners (Amazon Link).
In the book, I’ll give you a thorough overview of critical computer science topics such as machine learning, regular expression, data science, NumPy, and Python basics—all in a single line of Python code!
OFFICIAL BOOK DESCRIPTION: Python One-Liners will show readers how to perform useful tasks with one line of Python code. Following a brief Python refresher, the book covers essential advanced topics like slicing, list comprehension, broadcasting, lambda functions, algorithms, regular expressions, neural networks, logistic regression and more. Each of the 50 book sections introduces a problem to solve, walks the reader through the skills necessary to solve that problem, then provides a concise one-liner Python solution with a detailed explanation.