Photo by Igor Miske on Unsplash

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

  1. Slicing can not only be used for accessing range of elements but also for clearing, reversing and copying lists.
  2. Avoid using negative stride along with start and end indexes, if possible.

--

--

Rachit Tayal
Python Features

Sports Enthusiast | Senior Deep Learning Engineer. Python Blogger @ medium. Background in Machine Learning & Python. Linux and Vim Fan