Slicing in Python

Life is 10% what happens to us and 90% how we react to it. If you don't build your dream, someone else will hire you to help them build theirs.

Slicing in Python

It’s finally time to talk about slicing in Python. Slicing allows you to access one or more elements from some kind of sequence. We’ve had to hold my tongue while discussing the sequences earlier, where we have immutable sequences such as tuples, strings, and bytes, and mutable sequences like list and byte arrays. Slicing applies to being able to work with all of these types and with the mutable type, even modify that sequence. First of all, we see just some examples of those immutable sequences like a tuple, a string, a byte sequence, as well as mutable things like a list and a byte array. The first thing to know about slicing, is that you can use square brackets after the name of the object and put a number in there. The number is a index, where 0 refers to the first element, 1 would refer to the second element, 2 to the third element, 3 to the fourth element, and so on.

 # Slicing allows access one or more elements of a sequence 
 # Immutable sequences include tuples, strings, and bytes 
 a_tuple = ('a', 1, 2, (3, 4)) 
 a_string ='immutable' 
 a_bytes = b'sequence' 
 # Mutable sequences include lists and bytearrays 
 a_list = [5, 6, 7, 8, (4, 5)] 
 a_byte_array =bytearray(b'mutable') 
 # Accessing is allowed in all sequences
 print('a_tuple[0] ->', a_tuple[0]) 
 print('a_string[1] ->', a_string[1])
 print('a_bytes[2] ->', a_bytes[2]) 
 print('a_list[3] ->', a_list[3])
 print('a_byte_array[4] ->', a_byte_array[4]) 
 # Negative indexes are from the end 
 print('a_tuple[-1] ->', a_tuple[-1]) 
 print('a_string[-2] ->',a_string[-2]) 
 print('a_bytes[-3] ->', a_bytes[-3]) 
 print('a_list[-4]->', a_list[-4]) 
 print('a_byte_array[-5] ->', a_byte_array[-5]) 
 #Subslices can be accessed with two indexes 
 print('a_list[0:2] ->',a_list[0:2]) 
 print('a_list[:2] ->', a_list[:2]) 
 print('a_list[2:5] ->',a_list[2:5]) 
 print('a_list[2:] ->', a_list[2:]) 
 print('a_list[:] ->',a_list[:]) 
 list_ref = a_list 
 print('a_list is list_ref ->', a_list is list_ref) 
 list_copy = a_list[:] 
 print('a_list is list_copy ->', a_list is list_copy) 

So we see, in the output here, the very first element of a tuple, the second element of a string. In the case of bytes and bytes array, they return the byte value of that particular element. So q must have a byte value of 113. You can see that fourth element in a list, which is 8, the fifth element in a byte array, which means that that b must have a byte value of 98. You can also use negative slicing, where negative slicing works its way from the end instead of the beginning, where (-1) refers to the last element, (-2) the element before that, and so on.

 # Immutable sequences include tuples, strings, and bytes 
 a_tuple = ('a', 1, 2, (3, 4))
 a_string = 'immutable' 
 a_bytes = b'sequence' 
 # Mutable sequences include lists and bytearrays 
 a_list = [5, 6, 7, 8, (4, 5)] 
 a_byte_array =bytearray(b'mutable') 
 # Accessing is allowed in all sequences 
 print('a_tuple[0] ->',a_tuple[0]) 
 print('a_string[1] ->', a_string[1]) 
 print('a_bytes[2] ->',a_bytes[2]) 
 print('a_list[3] ->', a_list[3]) 
 print('a_byte_array[4] ->',a_byte_array[4]) 
 print('a_tuple[-1] ->',a_tuple[-1])
 print('a_string[-2] ->', a_string[-2]) 
 print('a_bytes[-3]->', a_bytes[-3]) 
 print('a_list[-4] ->', a_list[-4])
 print('a_byte_array[-5] ->', a_byte_array[-5])
 print('a_list[0:2] ->', a_list[0:2])
 
 The output : a_tuple[0] -> a 
 a_string[1] -> m
 a_bytes[2] -> 113 
 a_list[3] -> 8 
 a_byte-array[4] -> 98
 a_tuple[-1] -> (3,  4) 
 a_string[-2] -> 1 
 a_bytes[-3] -> 110 
 a_list[-4]-> 6 
 a_byte_array[-5] -> 116
 a_list[0 : 2] -> [5,6]

Another way of looking at this, is we might consider the first index to be zero-based and the second index to be 1-based. Looking at a_list between 0 and 2, we start at 0, go up to but not including the element with an index of 2, so we’d end up with those first two elements, 5 and 6. Now if you want to start at the beginning, you can omit the zero and just use colon and then the ending index. So notice the result is the same. Likewise, if you’re going to refer to the index just beyond the end, where here we have 0, 1, 2, 3, 4, 5 is beyond the end, remember it’s up to but not including that. This would give us from the element that has an index of 2, all the way through the end of that sequence. Instead of including that element that’s one beyond, basically, the end of your sequence, you can just omit that and use your starting index colon, and not specify the ending index that will go all the way to the end of the sequence.

Now if you use both an omission of the starting and ending indexes, then it creates a copy of the entire list. Now it’s important to know why you might do this. If you create an assignment of a variable to an object, it creates a reference to that object. And since the list is mutable, if I were to change a_list, it would appear changed in the reference to the list as well. You can use the is operator to see if two different references refer to the same object. And since a_list is list_ref is true, if you were to modify a_list, effectively you’d be modifying what you would see in list_copy.

 a_list = [5, 6, 7, 8, (4, 5)] 
 print('a_list[0:2] ->', a_list[0:2]) 
 print('a_list[:2] ->',a_list[:2]) 
 print('a_list[2:5] ->', a_list[2:5]) 
 print('a_list[2:] ->',a_list[2:]) 
 print('a_list[:] ->', a_list[:]) 
 list_ref = a_list
 print('a_list is list_ref ->', a_list is list_ref)
 
 The output : a_list[0 : 2] -> [5, 6]
 a_list[ : 2] -> [5, 6] 
 a_list[2 : 5] -> [7, 8, (4, 5)] 
 a_list[2 : ] -> [7, 8, (4, 5)]
 a_list[ : ] -> [5, 6, 7, 8, (4, 5)]
 a_list is list_ref-> True

By having a reference like list_ref = a_list, if you modify a_list then effectively you’d be modifying the list_ref list as well by creating a copy, by using that, what We call, full slice notation, to get a copy of the full slice. Then this is a separate object that does not refer to the same object as a_list. And if We modified a_list, it would not affect the elements that are in the list_copy. And you can see using the is operator to test if these two names reference the same object, it’s going to be false. By using a third parameter, you can specify a interval, or a step, that’s taken between each slice. By using [::2] for the slice notation, it will pick up every other element. And since the whole a_list shows this, we see 5, that first element, 7, the third element, and (4, 5), that fifth element, so it’s going every other element to create that sequence.

 list_copy = a_list[:]
 print('a_list is list_copy ->', a_list is list_copy) 
 print('a_list[::2] ->', a_list[::2]) 
 print('a_list[1:4:2] ->', a_list[1:4:2])
 print('a_string[::-1] ->', a_string[::-1])

 The output is:
 a_list is list_copy -> False 
 a_list[: : 2] -> [5, 6, (4, 5)]
 a_list[ 1: 4: 2] ->[6, 8]
 a_list[: : -1] -> elbatummi