Slice
In the section above, we have the assignment: list_2 = list_1
copies the name of the array, not its contents. In effect, the two names (list_1
and list_2
) identify the same location in the computer memory. Modifying one of them affects the other, and vice versa.
What Strategies Can Be Used to Address the Problem?
Fortunately, Python offers a simple yet powerful solution to make copies of lists or extract specific parts from them: slices. Slices are a fundamental element of Python syntax that enables you to create brand new copies of lists or select portions of a list with ease. What sets slices apart is that they copy the actual contents of the list, not just its name. This distinction is crucial for understanding the true power of slices. Let's take a closer look at a code snippet to illustrate their functionality:
list_1 = [1]
list_2 = list_1[:]
list_1[0] = 2
print(list_2) #Output: [1]
In this example, the list_1
contains a single element, which is then assigned to list_2
using slice notation [:]
. When we modify the first element of list_1
to 2
, we might expect list_2
to remain unchanged. However, thanks to slices, the output of this code will be [1]
. This seemingly small addition of [:]
creates a new list, independent of the original, ensuring that any modifications made to one list do not affect the other. Slices are a vital tool for working with lists in Python, providing flexibility and control over your data.
Basic Slicing in Python Lists
A key feature of slices in Python is their ability to extract specific portions of a list using a general syntax. The basic form of a slice is my_list[start:end], which may look similar to indexing, but the colon inside makes a significant difference as shown in figure below.
This type of slice creates a new list, referred to as the target list, by selecting elements from the source list based on the indices ranging from start
to end-1
. It's important to note that the ending index is not inclusive but represents the first element that is excluded from the slice. This behavior ensures that the resulting slice corresponds precisely to the desired range of elements. Furthermore, just like indexing, it is possible to use negative values for both the start and end indices, enabling you to conveniently extract elements from the list in reverse order or relative to the end of the list. Slices provide a versatile and intuitive mechanism for manipulating and extracting subsets of lists in Python.
Examples of Slicing in Python Lists
In the code snippet below, we demonstrate the usage of slicing in Python to create new lists or extract specific parts of existing lists:
# Copying the entire list.
list_1 = [1]
list_2 = list_1[:]
list_1[0] = 2
print(list_2)
# Copying some part of the list.
my_list = [10, 8, 6, 4, 2]
new_list = my_list[1:3]
print(new_list)
In the first part of the code, we create a list list_1
with a single element. To make a copy of the entire list, we use slicing with [:]
, which effectively duplicates the content of list_1
into list_2
. Modifying list_
1 by assigning a new value to its first element does not affect list_2
, as they are now separate lists. The output of the code will be [1]
, demonstrating that the contents of list_2
remain unaffected.
In the second part of the code, we have a list called my_list
with elements [10, 8, 6, 4, 2]
. By using the slice my_list[1:3]
, we create a new list new_list
that contains the elements at indices 1
and 2
from my_list
. The slicing excludes the element at index 3
, resulting in a new_list
with elements [8, 6]
. Running the code will print the new_list
as the output, demonstrating the extraction of a specific part of the original list.
Slice Syntax and Behavior
When using slicing in Python lists, the syntax my_list[start:end]
allows you to specify a range of elements to include in the slice. The start index represents the first element to be included, while the end index represents the first element that is not included in the slice. Negative indices can also be used to refer to elements relative to the end of the list.
When Start Index < End Index(e.g. my_list[1:-1]
)
If the start index is less than the end index (counting from the beginning of the list), the resulting slice will be as follow:
my_list = [10, 8, 6, 4, 2]
new_list = my_list[1:-1]
print(new_list)
The output of this snippet is [8, 6, 4]
. Here, the slice my_list[1:-1]
includes elements with indices 1
, 2
, and 3
, but excludes the element at index-1
.
When Start Index > End Index (e.g. my_list[-1:1]
)
If the start index is greater than the end index (counting from the beginning of the list), the resulting slice will be empty. For instance:
my_list = [10, 8, 6, 4, 2]
new_list = my_list[-1:1]
print(new_list)
The output of this snippet is an empty list []
, as the start index-1
refers to an element beyond the one described by the end index 1
.
In the example, we are trying to create a slice of my_list
using the indices -1
and 1
. The start index is -1
, which refers to the last element of the list, and the end index is 1
, which refers to the element at index 1 (the second element of the list).
Conclusion
However, when slicing a list, the start index must be less than the end index to define a valid range. In this case, the start index-1
is greater than the end index 1, counting from the beginning of the list. As a result, the resulting slice is empty because there are no elements within that range.
Therefore, when we print new_list
, we get an empty list []
as the output. It indicates that no elements are included in the slice due to the invalid range specified by the start and end indices.
When Omitting Start Index (e.g. my_list[:end]
)
When omitting the start index in a slice, it is assumed that you want to begin at the element with index 0
. This can be achieved using the slice my_list[:end]
, which is equivalent to my_list[0:end]
. Consider the following example:
my_list = [10, 8, 6, 4, 2]
new_list = my_list[:3]
print(new_list)
The output is [10, 8, 6], as the slice includes elements with indices 0, 1, and 2.
When Omitting End Index (e.g. my_list[start:]
)
Similarly, omitting the end index in a slice assumes that you want the slice to extend until the element with index len(my_list)
. This is equivalent to using my_list[start:]
, which is the same as my_list[start:len(my_list)]
. Take a look at the following snippet:
my_list = [10, 8, 6, 4, 2]
new_list = my_list[3:]
print(new_list)
The output is [4, 2]
, as the slice includes elements with indices 3
and 4
.
When Omitting Both Index (e.g. my_list[:]
)
Lastly, if both the start and end indices are omitted, it creates a copy of the entire list:
my_list = [10, 8, 6, 4, 2]
new_list = my_list[:]
print(new_list)
The output is [10, 8, 6, 4, 2]
, representing a copy of the original list.
The del
Instruction: Deleting Elements and Slices in Python Lists
The previously described del
instruction in Python can be used to delete not only individual elements from a list but also slices of elements. In the code snippet:
my_list = [10, 8, 6, 4, 2]
del my_list[1:3]
print(my_list)
We use del to remove a slice from my_list
. The slice specified is [1:3]
, which corresponds to elements at indices 1
and 2
(excluding the element at index 3
). The output of the snippet is [10, 4, 2]
, indicating that the elements within the specified slice have been deleted from the list.
Moreover, it is also possible to delete all the elements of a list at once using del
:
my_list = [10, 8, 6, 4, 2]
del my_list[:]
print(my_list)
By specifying del my_list[:]
, we remove all the elements from my_list
, resulting in an empty list. The output will be []
.
However, it's important to note that if the del
instruction is used to delete the list itself, rather than its content, it will lead to a runtime error when trying to access the list. Consider the following code:
my_list = [10, 8, 6, 4, 2]
del my_list
print(my_list)
Here, del my_list
deletes the list itself. As a result, trying to access my_list
in the print statement will cause a runtime error, indicating that the list no longer exists.