Slicing Tricks — lists, str, bytes
list slicing is one of the handy features Python provides.
--
Python includes syntax for slicing sequences into pieces. Slicing is commonly used to access range of elements within an ordered collection. The simplest use of slicing are the built-in types list
, str
, and bytes
. The basic form of slicing syntax is somelist[start:end:stride]
where start
is inclusive and end
is exclusive.
a = [1, 2, 3, 4, 5, 6, 7, 8]
print ('first four:', a[:4])
print ('last four:', a[-4:])
print ('middle two:', a[3:-3])
>>>
first four: [1, 2, 3, 4]
last four: [5, 6, 7, 8]
middle two: [4, 5]
When slicing from start, we should leave out the zero index to reduce the visual noise whereas while slicing to the end of list, leaving the final index is recommended.a[:5]
👍 is preferred over a[0:5]
🙅♂a[5:]
👍 is preferred over a[5:len(a)]
🙅
Using negative numbers for slicing is helpful for doing offsets relative to the end of the list.
# a = [1, 2, 3, 4, 5, 6, 7, 8]a[:] # [1, 2, 3, 4, 5, 6, 7, 8]
a[:5] # [1, 2, 3, 4, 5]
a[:-1] # [1, 2, 3, 4, 5, 6, 7]
a[4:] # [5, 6, 7, 8]
a[-3:] # [6, 7, 8]
a[2:5] # [3, 4, 5]
a[2:-1] # [3, 4, 5, 6, 7]
a[-3:-1] # [6, 7]
Slicing deals properly with start and end indexes that are beyond the boundaries of the list.
first_twenty_items = a[:20]
last_twenty_items = a[-20:]
In contrast, accessing the same index directly will cause an exception.
# a = [1, 2, 3, 4, 5, 6, 7, 8]
a[20]
>>>
IndexError: list index out of range
NOTE: we can get surprising results from slicing in some scenarios. Try a[-0:]
😅
The result of slicing a list is a whole new list. References to the objects from the original list are maintained. Modifying the result of slicing won’t affect the original list.
# a = [1, 2, 3, 4, 5, 6, 7, 8]b = a[4:]
print ('before: ', b)
b[1] = 99
print ('after: ', b)
print ('no change: ', a)>>>
before: [5, 6, 7, 8]
after: [5, 99, 7, 8]
no change: [1, 2, 3, 4, 5, 6, 7, 8]
When used in assignments, slices will replace the specified range in the original list. The length of slice assignments don’t need to be the same. The list will grow or shrink to accommodate the new values.
# a = [1, 2, 3, 4, 5, 6, 7, 8]print ('before: ', a)
a[2:7] = [99, 22, 14]
print ('after: ', a)
>>>
before: [1, 2, 3, 4, 5, 6, 7, 8]
after: [1, 2, 99, 22, 14, 8]
Using slicing, we can also create copies of list
>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b = a[:]
>>> b is a
False
>>> b
[1, 2, 3, 4, 5, 6, 7, 8]>>> c = a
>>> c is a
True
>>> c
[1, 2, 3, 4, 5, 6, 7, 8]
>>> c.append(9)
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Specifying start
, end
, and stride
in a slice can be used to reverse a list.
# a = [1, 2, 3, 4, 5, 6, 7, 8]
>>> b = a[::-1]
>>> b
[8, 7, 6, 5, 4, 3, 2, 1]
But, how much useful are negative strides besides -1. Consider the following example.
a # [1, 2, 3, 4, 5, 6, 7, 8]
a[::2] # [1, 3, 5, 7]
a[::-2] # [8, 6, 4, 2]
Here, ::2
means select every second item starting at the beginning whereas ::-2
means select every second item starting at the end and moving backwards.
It will only complicate things if we have slicing like 2::2
, -2::-2
, -2:2:-2
, 2:2:-2
😕
a[2::2] # [3, 5, 7]
a[-2::-2] # [7, 5, 3, 1]
a[-2:2:-2] # [7, 5]
a[2:2:-2] # []
stride
part of slicing syntax can be confusing, especially if the stride
is negative. Using it can make our code less maintainable.
Key Highlights
- Slicing can not only be used for accessing range of elements but also for clearing, reversing and copying lists.
- Avoid using negative
stride
along withstart
andend
indexes, if possible.