Python Slicing Mastery: All the Rules You Need to Know

Introduction

Python slicing is a powerful syntax for extracting specific portions of indexable objects.

Indexable objects (also called indexables or subscriptable objects) associate a specific value with an integer index and can return it using the square bracket notation []. Examples of indexable objects include lists, strings, tuples, sets, and ranges.

Slicing provides a convenient way to work with segments of a larger collection.

Python Slicing Basics

The slicing syntax consists of the sliced object followed by square brackets []. The square brackets contain up to three values (shown below as ‘begin,’ ‘end,’ and ‘step’) separated by colons :.

indexable_object[begin:end:step]

The components of the syntax are as follows:

  • indexable_object: the object being sliced
  • begin: the starting index of the slice
  • end: the ending index (not included in the slice)
  • step: the interval between the items

Slicing creates a new object of the same type that contains the “sliced” portion of the original. The original object remains unchanged.

It’s important to note that even though both slicing and indexing syntax use square brackets, they are not the same thing. If it has a colon, then it’s slicing; otherwise, if it’s just a single value with no colon, it’s indexing.

In the following sections, you will learn all the rules for using the slicing syntax to create the slices you want. Once you learn how slicing works, you can apply it the same way to all objects that support it.

Slicing With ‘begin’ and ‘end’

The ‘begin’ index specifies the starting point of the slice. It marks the first element to include in the result.

The ‘end’ index indicates where the slice should stop, but it does not include the element at the ‘end’ index itself.

As indexes, the ‘begin’ and ‘end’ of a slice also reference the very first element of the sliced object as element 0.

Example:

my_list = [10, 20, 30, 40, 50, 60]
sublist = my_list[1:4]
print(sublist)

Output:

[20, 30, 40]

The example above begins the slice at index 1, which has the value 20. It includes items up to, but not including index 4, which has the value 50. Therefore, the last item included is 40.

Note that ‘step’ was not used in the example. (You’ll learn about it next.) If you are not using ‘step,’ you can omit the second colon.

The ‘step’

The ‘step’ value is optional and controls how many elements are skipped during slicing. It allows you to extract items at regular intervals.

Example:

my_list = [10, 20, 30, 40, 50, 60]
sublist = my_list[1:5:2]
print(sublist)

Output:

[20, 40]

The example above begins the slice at index 1 (value 20) and ends one before index 5 (value 50). This slice would have resulted in [20, 30, 40, 50] if it were not for ‘step’ being 2. Because ‘step’ is 2, only every second item is taken, leaving us with [20, 40].

‘begin,’ ‘end,’ and ‘step’ Are All Optional

You can omit ‘begin,’ ‘end,’ or ‘step’ in your slice, and Python will apply the following defaults:

  • ‘begin’ defaults to 0, to start at the first item.
  • ‘end’ defaults to the length of the collection, to include the last item of the list.
  • ‘step’ defaults to 1, to not skip anything.

If you omit ‘step,’ the last colon is not necessary.

If you omit both ‘end’ and ‘step,’ the slice requires one colon on the right; otherwise, Python will think you’re indexing.

Example:

my_list = [10, 20, 30, 40, 50, 60]
sublist1 = my_list[:3]  
sublist2 = my_list[2:]  
sublist3 = my_list[::3]  

print(sublist1)
print(sublist2)
print(sublist3)

Output:

[10, 20, 30]
[30, 40, 50, 60]
[10, 40]

Omitting All Slice Values With [:]

It’s also possible to omit all slice values by leaving a single colon inside the brackets [:]. Two colons will also work but are unnecessary. This will create a copy of the original object if the object is mutable or return the same object if it’s not.

Example:

my_list = [10, 20, 30]
copied_list = my_list[:]
print(copied_list)
print(copied_list is my_list)

Output:

[10, 20, 30]
False

In this example, the mutable list object, my_list, is copied using [:]. Even though copied_list has the same values as the original my_list, it’s not the same object, as indicated by the False value returned by the is operator. (The Python is operator tests object identity, i.e. whether two things are the same object or not).

Example:

my_tuple = (10, 20, 30)
copied_tuple = my_tuple[:]
print(copied_tuple)
print(copied_tuple is my_tuple)

Output:

(10, 20, 30)
True

In this second example, the immutable tuple object, my_tuple, is copied using [:]. And this time copied_tuple has the same values as the original and is the same object, as indicated by the True value returned by the is operator.

Negative ‘begin’ or ‘end’

You can use negative values with ‘begin’ and ‘end’ to specify positions from the end of the collection. -1 represents the last item, -2 the next-to-last, and so on.

Example:

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[0:-2])

Output:

[10, 20, 30, 40]

The above example removes the last two items in the list by using -2 for the ‘end’ part of the slice.

Negative ‘step’ for Reversed Slicing

A negative ‘step’ value allows you to reverse the order of the slice.

When ‘step’ is negative, the slice starts at the ‘start’ index, goes backward in strides of size ‘step,’ and stops one before the ‘end’ index.

Since it’s going backward, the ‘start’ index should be after the ‘end’ index in the collection, otherwise, the slice will be empty.

Example:

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[5:0:-2])

Output:

[60, 40, 20]

This example begins the slice at index 5, goes backward by 2, and stops before index 0.

A negative step allows for a neat way to reverse a list. A [::-1] slice will create a new collection that’s the exact reverse of the original.

Example:

my_list = [10, 20, 30, 40]
print(my_list[::-1])

Output:

[40, 30, 20, 10]

Out-of-Bounds Slicing

Unlike indexing, out-of-bounds slicing does not generate an error. Python gracefully handles such situations by ignoring out-of-bounds locations.

Example:

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[2:100])

Output:

[30, 40, 50, 60]

This example slices way beyond the end of the list, but no error is generated, and the whole collection is returned. The sliced list is the maximum available slice without going out of bounds of the original list.

The slice() Constructor

In addition to using the slicing syntax with square brackets, Python provides a built-in slice() constructor that allows you to create slice objects programmatically. The slice() constructor can be particularly useful when you need to generate slices dynamically, reuse on multiple objects, or store them for later use.

The slice() constructor takes up to three arguments: start, stop, and step, just like the components of the square-bracket slicing syntax. Here’s the general syntax of the slice() constructor:

slice(start, stop, step)

Each of the arguments to slice() is optional, but at least one is expected. You can omit one or two arguments from the right or pass a None to have the arguments assume their default values.

You apply a slice created with the slice() constructor by placing it in the square brackets instead of the colon-separated values.

Example:

my_list = [10, 20, 30, 40, 50, 60]
my_slice = slice(1, 5, 2)
sublist = my_list[my_slice]
print(sublist)  

Output:

[20, 40]

The example above first creates the slice object my_slice using the slice() constructor. It then uses the slice object to extract a specific portion of my_list without specifying the slicing parameters directly within the square brackets.

The next example demonstrates how to use default values as well as reuse a slice created with the slice() constructor.

Example:

str1 = "I'm going in reverse."
str2 = "I'm going backward."
reverse = slice(None, None, -1)
print(str1[reverse])
print(str2[reverse])

Output:

.esrever ni gniog m'I
.drawkcab gniog m'I

The example above creates a slice called reverse that can reverse any indexable object. The ‘begin’ and ‘end’ parameters of the slice are both None, signifying that the slice spans the whole collection. The reverse slice is then used on two strings to reverse them.

String Slicing in Python

Python users often mistakenly think that there is a different concept called “string slicing” in Python. However, string slicing is exactly the same as slicing for any other indexable and follows the same rules you’ve seen so far. Python strings are indexable on their individual characters and behave like lists of characters.

Example:

my_string = "PythonString"
substring = my_string[0:6]
print(substring) 

Output

Python

The example above extracts the word “Python” from the given string using slicing.

Summary & Reference for Python Slicing

The Python slicing syntax is for extracting specific portions of indexable objects such as lists, strings, tuples, sets, and ranges. It allows you to easily create segments of larger collections.


This is the slicing syntax:

indexable_object[begin:end:step]

In the above syntax, ‘begin’, is the starting index of the slice, ‘end’ indicates where the slice should end, excluding the element at the ‘end’ index, and ‘step’ controls the interval between items.

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[1:4:2])  # --> [20, 40]

Slicing creates a new object of the same type containing the sliced portion, leaving the original object unchanged.


Each of the three values can be omitted to assume defaults, which are 0 for ‘begin’, the length of the list for ‘end’, and 1 for ‘step’.

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[:3])  # --> [10, 20, 30]
print(my_list[2:])  # --> [30, 40, 50, 60]

You can even omit all slice values by using [:]. This creates a copy of the original object for mutable objects or returns the same object for immutable ones.

my_list = [10, 20, 30, 40, 50, 60]
copied_list = my_list[:]  # copied_list --> [10, 20, 30, 40, 50, 60]

Negative values for ‘begin’ and ‘end’ allow you to specify indexes from the end of the collection.

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[-4:-2])  --> [30, 40]

A negative ‘step’ value enables reversed slicing, where the slice starts at ‘start,’ goes backward by ‘step,’ and stops one before ‘end.’ Consequently, [::-1] reverses the original collection.

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[5:0:-2]) # --> [60, 40, 20]
print(my_list[::-1])  # --> [60, 50, 40, 30, 20, 10]

Python gracefully handles out-of-bounds slicing by ignoring locations beyond the collection’s bounds. This ensures smooth and error-free slicing operations.

my_list = [10, 20, 30, 40, 50, 60]
print(my_list[2:100])  # --> [30, 40, 50, 60]

Python provides a built-in slice() constructor that allows you to create slice objects programmatically. It takes the same arguments as the square bracket notation, but with function syntax. At least one argument is required, and you can omit one or two arguments from the right or pass a None to have the arguments assume their default values. You can apply a slice object by placing it in the square brackets instead of the colon-separated values.

str1 = "I'm going in reverse."
reverse = slice(None, None, -1)
print(str1[reverse])  # --> .esrever ni gniog m'I

String slicing in Python is the same slicing as for all other indexable objects.