From The Quick Python Book, Third Edition by Naomi Ceder
Understanding how list indices and lists work in Python makes the language a lot more useful. This article excerpt discusses list indices and modifying lists in Python. |

Save 37% on *The Quick Python Book, Third Edition*. Just enter code **fccceder** into the discount code box at checkout at manning.com.

**List indices**

Understanding how list indices work makes Python much more useful.

Elements can be extracted from a Python list using a notation like C’s array indexing. Like C and many other languages, Python starts counting from zero; asking for element zero returns the first element of the list, asking for element one returns the second element, and so forth. Here are a few examples:

>>> x = ["first", "second", "third", "fourth"] >>> x[0] 'first' >>> x[2] 'third'

But Python indexing is more flexible than C indexing; if indices are negative numbers, they indicate positions counting from the end of the list, with negative one being the last position in the list, negative two being the second-to-last position, and so forth. Continuing with the same list x, we can do the following:

>>> a = x[-1] >>> a 'fourth' >>> x[-2] 'third'

For operations involving a single list index, it’s generally satisfactory to think of the index as pointing at a specific element in the list. For more advanced operations, it’s more correct to think of list indices as indicating positions *between* elements. In the list `["first", "second", "third", "fourth"]`

, you can think of the indices as pointing like this:

x =[ | “first”, | “second”, | “third”, | “fourth” | ] | |||||

Positive indices | 0 | 1 | 2 | 3 | ||||||

Negative indices | –4 | –3 | –2 | –1 |

This is irrelevant when you’re extracting a single element, but Python can extract or assign to an entire sublist at once – an operation known as *slicing*. Instead of entering `list[index]`

to extract the item after `index`

, enter `list[index1:index2]`

to extract all items including `index1`

and up to (but not including) `index2`

into a new list. Here are some examples:

>>> x = ["first", "second", "third", "fourth"] >>> x[1:-1] ['second', 'third'] >>> x[0:3] ['first', 'second', 'third'] >>> x[-2:-1] ['third']

It may seem reasonable that if the second index indicates a position in the list *before* the first index, this would return the elements between those indices in reverse order, but this isn’t what happens. Instead, this returns an empty list:

>>> x[-1:2] []

When slicing a list, it’s also possible to leave out `index1`

or `index2`

. Leaving out `index1`

means “go from the beginning of the list,” and leaving out `index2`

means “go to the end of the list”:

>>> x[:3] ['first', 'second', 'third'] >>> x[2:] ['third', 'fourth']

Omitting both indices makes a new list that goes from the beginning to the end of the original list; it copies the list. This is useful when you wish to make a copy that you can modify, without affecting the original list:

>>> y = x[:] >>> y[0] = '1 st' >>> y ['1 st', 'second', 'third', 'fourth'] >>> x ['first', 'second', 'third', 'fourth']

Try This: list slices and indexes

Using what you know about the len() function and list slices, how would you combine the two to get the second half of a list when you don’t know what size the list is? Experiment in the Python shell to confirm if your solution works.

**Modifying lists**

You can use list index notation to modify a list as well as to extract an element from it. Put the index on the left side of the assignment operator:

>>> x = [1, 2, 3, 4] >>> x[1] = "two" >>> x [1, 'two', 3, 4]

Slice notation can be used here too. Saying something like `lista[index1:index2] = listb`

causes all elements of `lista`

between `index1`

and `index2`

to be replaced with the elements in `listb`

. `listb`

can have more or fewer elements than are removed from `lista`

, in which case the length of `lista`

is altered. You can use slice assignment to do several different things, as shown here:

>>> x = [1, 2, 3, 4] >>> x[len(x):] = [5, 6, 7] ❶ >>> x [1, 2, 3, 4, 5, 6, 7] >>> x[:0] = [-1, 0] ❷ >>> x [-1, 0, 1, 2, 3, 4, 5, 6, 7] >>> x[1:-1] = [] ❸ >>> x [-1, 7]

❶ Appends list to end of list

❷ Appends list to front of list

❸ Removes elements from list

Appending a single element to a list is such a common operation that there’s a special `append`

method to do it:

>>> x = [1, 2, 3] >>> x.append("four") >>> x [1, 2, 3, 'four']

One problem can occur if you try to append one list to another. The list gets appended as a single element of the main list:

>>> x = [1, 2, 3, 4] >>> y = [5, 6, 7] >>> x.append(y) >>> x [1, 2, 3, 4, [5, 6, 7]]

The `extend`

method is like the `append`

method, except that it allows you to add one list to another:

>>> x = [1, 2, 3, 4] >>> y = [5, 6, 7] >>> x.extend(y) >>> x [1, 2, 3, 4, 5, 6, 7]

A special `insert`

method allows you to insert new list elements between two existing elements or at the front of the list. `insert`

is used as a method of lists and takes two additional arguments; the first is the index position in the list where the new element should be inserted, and the second is the new element itself:

>>> x = [1, 2, 3] >>> x.insert(2, "hello") >>> print(x) [1, 2, 'hello', 3] >>> x.insert(0, "start") >>> print(x) ['start', 1, 2, 'hello', 3]

`insert`

understands list indices in its own way, but for most uses it’s easiest to think of `list.insert(n, elem)`

as meaning `insert elem`

prior to the *n*th element of list. `insert`

is a convenience method. Anything that can be done with `insert`

can also be done using slice assignment; `list.insert(n, elem)`

is the same thing as `list[n:n] = [elem]`

when `n`

is non-negative. Using `insert`

makes for more readable code, and `insert`

even handles negative indices:

>>> x = [1, 2, 3] >>> x.insert(-1, "hello") >>> print(x) [1, 2, 'hello', 3]

The `del`

statement is the preferred method of deleting list items or slices. It doesn’t do anything that can’t be done with slice assignment, but it’s usually easier to remember and easier to read:

>>> x = ['a', 2, 'c', 7, 9, 11] >>> del x[1] >>> x ['a', 'c', 7, 9, 11] >>> del x[:2] >>> x [7, 9, 11]

In general, `del list[n]`

does the same thing as `list[n:n+1] = []`

, whereas `del list[m:n]`

does the same thing as `list[m:n] = []`

.

The `remove`

method isn’t the converse of `insert`

. Whereas `insert`

inserts an element at a specified location, `remove`

looks for the first instance of a given value in a list and removes that value from the list:

>>> x = [1, 2, 3, 4, 3, 5] >>> x.remove(3) >>> x [1, 2, 4, 3, 5] >>> x.remove(3) >>> x [1, 2, 4, 5] >>> x.remove(3) Traceback (innermost last): File "<stdin>", line 1, in ? ValueError: list.remove(x): x not in list

If `remove`

can’t find anything to remove, it raises an error. You can catch this error using the exception-handling abilities of Python, or you can avoid the problem by using `in`

to check for the presence of something in a list before attempting to remove it.

The `reverse`

method is a more specialized list modification method. It efficiently reverses a list in place:

>>> x = [1, 3, 5, 6, 7] >>> x.reverse() >>> x [7, 6, 5, 3, 1]

**Try this: Modifying lists**

Suppose you have a list which is ten items long. How might you move last three items from the end of the list to the beginning, keeping them in the same order?

That’s all for lists!

For more, check out the whole book on liveBook here and see this Slideshare Presentation.