-
Python Slices
Python slices are very useful and you most certainly get acquainted with it at the early stages of language learning. But not many of you know how flexible this tool actually is and how many things you can do with Python slice.
At the time of this article publication the latest stable Python version was Python3.6 which is supported in CheckiO, so all of these examples you can try for yourself.
The basics of slicing
Let's assume that we have a list of strings.
>>> letters = ['c', 'h', 'e', 'c', 'k' , 'i', 'o']
We can get a specific element of this list. (It is not a slicing yet, but it illustrates how index works in Python)
>>> letters[2] 'e' >>> letters[1] 'h' >>> letters[0] 'c' >>> letters[-2] 'i'
Take notice that the index starts with a 0, so to obtain the first element you should request the zero one. You can use negative indexes for starting count from the end.
Now the actual slices.
You can use the following formats for slicing in Python.
letters[start_pos:] letters[start_pos:end_pos] letters[:end_pos] letters[::step] letters[start_pos::step] letters[start_pos:end_pos:step] letters[:end_pos:step]
Let's get back to our examples.
>>> letters = ['c', 'h', 'e', 'c', 'k' , 'i', 'o'] >>> letters[2:] ['e', 'c', 'k', 'i', 'o'] >>> letters[:2] ['c', 'h'] >>> letters[0:2] ['c', 'h']
Observe two things here. The first one is that 0:2 and :2 - give the same result. And the first element of the slice is included in the result when the second one isn't. For this reason here is another example where we cut off the first two elements from the list and put those elements into another list
>>> fist_two, letters = letters[:2], letters[2:] >>> letters ['e', 'c', 'k', 'i', 'o'] >>> fist_two ['c', 'h']
Negative numbers can be present in a slice, which means that the countdown will be going from the end.
>>> letters = ['c', 'h', 'e', 'c', 'k' , 'i', 'o'] >>> letters[-2:] ['i', 'o'] >>> letters[:-2] ['c', 'h', 'e', 'c', 'k'] >>> letters[-6:-4] ['h', 'e']
Slice always creates a separate copy of the elements' list. So you can make a copy of all of the elements in two ways.
>>> letters.copy() ['c', 'h', 'e', 'c', 'k', 'i', 'o'] >>> letters[:] ['c', 'h', 'e', 'c', 'k', 'i', 'o']
As you can see if you don't specify the starting and ending elements then the zero and the last one will be taken, respectively. But this still won't create a copy of the nested arrays. You can read about it in our previous article - 10 common beginner mistakes in Python.
If you need to change step and order of the return array then use the third element of the slice, called step.
>>> letters[::-1] ['o', 'i', 'k', 'c', 'e', 'h', 'c']
All of the elements are given in reverse order.
>>> letters[::2] ['c', 'e', 'k', 'o'] >>> letters[1::2] ['h', 'c', 'i']
Let's derive all odd and even indexes.
Here is a small illustration that can help you to understand how the start position, end position and step are used in Python slice.
If the slices' elements will go beyond the scope of the list itself there will be no mistake.
>>> letters[100:] [] >>> letters[:100] ['c', 'h', 'e', 'c', 'k', 'i', 'o'] >>> letters[100:100] [] >>> letters[3:2] []
To this point we've received data from the list, which means that this kind of slicing operations could be conducted also with the immutable data types, like tuple and str. In that case the result of the slice will be of the corresponding data type.
>>> line = 'coding game' >>> line[:6] 'coding' >>> version = (3, 6, 3) >>> version[:2] (3, 6)
But slices in Python can be also used for the operations of replacement or adding of elements. For this let's create a new array which consists of numbers going up in order and use a range function to do that. In Python 3 this function returns Iterator, that's why with the help of a list function we’re creating list right away.
>>> range(10) range(0, 10) >>> numbers = list(range(10)) >>> numbers [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I'll remind you that we can just replace elements one by one not using slices.
>>> numbers[0] = 'one' >>> numbers ['one', 1, 2, 3, 4, 5, 6, 7, 8, 9]
Now let's look what we can do if a slice will be to the left of the equal sign.
>>> numbers[3:5] = ['three', 'four'] >>> numbers ['one', 1, 2, 'three', 'four', 5, 6, 7, 8, 9] >>> numbers[6:] = ['six'] >>> numbers ['one', 1, 2, 'three', 'four', 5, 'six'] >>> numbers[2:3] = ['2.1', '2.2', '2.3', '..'] >>> numbers ['one', 1, '2.1', '2.2', '2.3', '..', 'three', 'four', 5, 'six']
A small trick. How to replace the whole list but without loosing link on it
>>> a = [1,2,3] >>> b = a >>> a [1, 2, 3] >>> b [1, 2, 3] >>> a[:] = [5,6,7,8,9] >>> a [5, 6, 7, 8, 9] >>> b [5, 6, 7, 8, 9]
As you can see from the last two operations the quantity of elements on the left and right sides may not be the same and Python will easily handle it.
Slice object
There is a builtin slice function in Python which allows you make a cut-off first and then apply it to an array. For this we'll use already familiar to us list.
>>> letters = ['c', 'h', 'e', 'c', 'k' , 'i', 'o'] >>> hehe = slice(1,3) >>> hehe slice(1, 3, None) >>> letters[hehe] ['h', 'e'] >>> hehe.start 1 >>> hehe.stop 3
The slice function will help to improve the code readability in case array's slice is complex. It can be estimated separately and then applied, which will be much easier than to contain the complex expression in square brackets or using along 3 separate variables.
I have found for you on CheckiO a relatively small solution from gyahun_dash using slice.
Custom slices
We can use slices in classes defining the __getitem__, __setitem__, and __delitem__ methods.
Here is an example where I'm creating a inheritor from list but with one restriction - I'm not allowing to use step in slicing for its children.
class SolidList(list): def __getitem__(self, key): if isinstance(key, slice) and key.step and key.step != 1: raise ValueError("You should always use step = 1") return super(SolidList, self).__getitem__(key) letters = SolidList('checkio') print(letters[1]) print(letters[1:3]) print(letters[1:2:-1])
The output of this script will be:
h ['h', 'e'] Traceback (most recent call last): … ValueError: You should always use step = 1
Slice is not just a copy
Slicing has one slight problem - it always returns the copy. And it's not such a great idea, especially when you need to work with a large data arrays.
But Python has a solution here. The function islice from itertools module returns an iterator instead of a slice copy.
from itertools import islice letters = 'Slicing in Python' for ch in islice(letters, 2, 5): print(ch)
Moreover, at input the function islice also gets an iterator as an argument.
The following example raises an exception:
letters = ['c', 'h', 'e', 'c', 'k', 'i', 'o'] for item in reversed(letters)[:3]: print(item)
TypeError: 'list_reverseiterator' object is not subscriptable
And this is where islice might also be useful and can fix our previous solution.
from itertools import islice letters = ['c', 'h', 'e', 'c', 'k', 'i', 'o'] for item in islice(reversed(letters),3): print(item)
And, of course, a creative solution from our user LLluma on the topic of islice.
So, this is it with Python slices. Thank you for following our publications. We'll continue bringing to your attention the most relevant, to our opinion, insights we've came across. If you have some interesting solutions or moves where slicing was used, please, do share them in comments under the article, I'll be glad to use those to extend the article.
Welcome to CheckiO - games for coders where you can improve your codings skills.
The main idea behind these games is to give you the opportunity to learn by exchanging experience with the rest of the community. Every day we are trying to find interesting solutions for you to help you become a better coder.
Join the Game