Imagine having a long loaf of bread. Sometimes, you want the whole thing, but other times you only need a few slices. List slicing in Python works in a similar way—it lets you extract specific portions of a list rather than working with the entire list at once. This is incredibly useful when dealing with large datasets, manipulating specific ranges of data, or simply extracting the information you need.
To perform slicing, you use square brackets []
along with a special syntax. This syntax involves specifying the starting index (where your slice begins), the stopping index (where it ends), and the step size (how many elements you skip between each included element). With this simple technique, you can access elements from the beginning, middle, or end of a list, modify, remove, or insert elements in a list, and even create copies of entire lists.
Throughout this tutorial, we’ll learn how this syntax works and explore examples of different slicing techniques. Let’s get started!
Syntax
The basic syntax for slicing a list is:

start
: The index at which the slice begins (inclusive). If omitted, the slice starts from the beginning of the list.stop
: The index at which the slice ends (exclusive). If omitted, the slice goes up to the end of the list.step
: The interval between elements in the slice. If omitted, the default step is 1.
So, if you have a list named L, writing L[start:stop:step]
gives you a new list containing the elements from L starting at the start
index, going up to (but not including) the stop
index, and taking elements in steps of step
.
Basic Slicing
Let’s look at a simple example of list slicing. Imagine you have a list of letters named L:
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
Now, let’s say you want to extract a portion of this list, specifically the elements from ‘c’ to ‘g’. You can do this using slicing:
print(L[2:7])
# Output: ['c', 'd', 'e', 'f', 'g']
In this example, we start at index 2, which corresponds to the letter ‘c’. Then we slice up to, but not including, index 7. This means we stop just before the letter ‘h’ at index 7. The result is a new list that contains the elements at indices 2 through 6, giving us the desired sequence of letters from ‘c’ to ‘g’.

Slicing with Negative Indices
When slicing lists in Python, you can use negative indices as well. This allows you to reference elements from the end of the list. For example, the index -1 represents the last element, -2 represents the second-to-last element, and so on.
Consider the same list of letters. If you wanted to extract the elements from ‘c’ to ‘g’ using negative indices, you could write:
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[-7:-2])
# Output: ['c', 'd', 'e', 'f', 'g']

Slicing with Positive and Negative Indices
Python allows you to mix positive and negative indices within the same slice. This can be particularly useful when you want to select elements relative to both the beginning and the end of your list.
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[2:-5])
# Output: ['c', 'd']
Slicing with a Step
In addition to specifying start
and stop
indices, slicing also allows you to introduce a step
value, enabling you to extract elements at regular intervals.
Let’s take our familiar list of letters as an example. If you want to get every other element starting from index 2 (the letter ‘c’) and going up to, but not including, index 7, you can use a step
value of 2:
# Extract every 2nd item between position 2 to 7
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[2:7:2])
# Output: ['c', 'e', 'g']

Negative Step
A negative step
reverses the order of elements. In the example below, the slice starts at index 6 and ends before index 1, taking every second element in reverse order.
# Return every 2nd item between position 6 to 1
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[6:1:-2])
# Output: ['g', 'e', 'c']
When using a negative step
, ensure the start
index is greater than the stop
index.
Slice at the Beginning and to the End
When you omit the start
index, the slice begins at the start of the list. So, L[:stop]
is equivalent to L[0:stop]
.
For example, to get the first three items of a list named L, you would use L[:3]
.
# Slice the first three items from the list
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[:3])
# Output: ['a', 'b', 'c']
On the other hand, when you omit the stop
index, the slice goes up to the end of the list. So, L[start:]
is equivalent to L[start:len(L)]
.
For example, to obtain the last three items, you’d use L[6:]
.
# Slice the last three items from the list
L = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
print(L[6:])
# Output: ['g', 'h', 'i']
Reversing a List
Omitting both the start
and stop
indices while specifying a negative step
value of -1 reverses the order of elements in the slice.
L = ['a', 'b', 'c', 'd', 'e']
print(L[::-1])
# Output: ['e', 'd', 'c', 'b', 'a']
Replacing a Slice
Slicing can also be used to modify lists. You can replace elements within a list by assigning a new list to a slice of the original list.
For example, if you want to replace the elements from index 1 to 3 (inclusive) with the values 1, 2, and 3, you can do:
# Modify multiple list items
L = ['a', 'b', 'c', 'd', 'e']
L[1:4] = [1, 2, 3]
print(L)
# Output: ['a', 1, 2, 3, 'e']
Interestingly, you can replace a single element with multiple elements using this technique.
# Replace multiple elements in place of a single element
L = ['a', 'b', 'c', 'd', 'e']
L[1:2] = [1, 2, 3]
print(L)
# Output: ['a', 1, 2, 3, 'c', 'd', 'e']
Inserting Elements
You can efficiently insert new elements into a list without replacing existing ones by specifying a zero-length slice.
To insert elements at the beginning of a list, you can use a slice starting from the beginning (L[:0]
) and assign the new elements to it.
# Insert at the start
L = ['a', 'b', 'c']
L[:0] = [1, 2, 3]
print(L)
# Output: [1, 2, 3, 'a', 'b', 'c']
If you want to insert elements at the end, you can use a slice that starts at the current length of the list (L[len(L):]
) and assign the new elements there.
# Insert at the end
L = ['a', 'b', 'c']
L[len(L):] = [1, 2, 3]
print(L)
# Output: ['a', 'b', 'c', 1, 2, 3]
Inserting in the middle of the list is also possible. By using a slice with the same start
and stop
index, you can insert elements at the desired position within the list without overwriting any existing elements.
# Insert in the middle
L = ['a', 'b', 'c']
L[1:1] = [1, 2, 3]
print(L)
# Output: ['a', 1, 2, 3, 'b', 'c']
Removing a Slice
You can remove multiple elements from the middle of a list using slicing techniques. One way to do this is by assigning an empty list to the desired slice.
# Remove elements from index 1 to 5 (exclusive)
L = ['a', 'b', 'c', 'd', 'e']
L[1:5] = []
print(L)
# Output: ['a']
Alternatively, you can use the del
statement to achieve the same result.
# Remove elements from index 1 to 5 (exclusive)
L = ['a', 'b', 'c', 'd', 'e']
del L[1:5]
print(L)
# Output: ['a']
Copying the Entire List
In Python, when you assign one list to another (e.g., new_list = old_list
), you’re not creating a true copy. Instead, you’re creating a new reference that points to the same underlying list object. Any changes made to either new_list
or old_list
will affect both, as they share the same data.
To create an actual copy of the list, you can use the slicing operator. By omitting both the start
and stop
indices (L1[:]
), you create a copy of the entire list L1. This means L2 is a new list object containing the same elements as L1, but changes to one list won’t affect the other.
L1 = ['a', 'b', 'c', 'd', 'e']
# Create a shallow copy of L1
L2 = L1[:]
print(L2)
# Output: ['a', 'b', 'c', 'd', 'e']
print(L2 is L1)
# Output: False (Confirms they are different list objects)
It’s important to note that this slicing technique creates a shallow copy. This means a new list is made, but if the original list contains mutable objects (like nested lists or dictionaries), those objects are not duplicated. Both lists share references to the same mutable objects.
For deeper copies where nested mutable objects are also duplicated, you can use the list.copy()
method or the copy.deepcopy()
function (if you need to copy nested objects recursively).
Important Considerations
In list slicing, certain considerations are important to avoid unexpected behavior.
Out of Range Indices
When you use indices that go beyond the boundaries of a list, Python adjusts the indices to the nearest valid values instead of causing an error. For example:
# Attempt to slice up to index 100 (which doesn't exist)
L = ['a', 'b', 'c', 'd', 'e', 'f']
print(L[3:100])
# Output: ['d', 'e', 'f']
Here, L[3:100]
starts at index 3 and attempts to go up to index 100. Since the list ends before index 100, the slice includes all elements from index 3 to the end.
Empty Slice
If you try to create a slice where the starting index is the same as or greater than the stopping index (and the step
is positive), you’ll get an empty list:
# Attempt to slice starting at index 5 and ending at index 3
L = ['a', 'b', 'c', 'd', 'e', 'f']
print(L[5:3])
# Output: []
Step Size of 0
Specifying a step
size of 0 is not allowed and will result in a ValueError
:
L = ['a', 'b', 'c', 'd', 'e', 'f']
print(L[1:4:0])
# ValueError: slice step cannot be zero