Lists are ordered and mutable collections of non-unique items.

In other programming languages, lists are sometimes called arrays.

List basics

Lists are delimited by square brackets and can contain different types of items:

>>> list1 = [1, -20, "hello", 4.347, 500]

Accessing list items

List items can be accessed by their index or position. In Python and most other programming languages, the first item has index 0 (zero):

>>> list1[0] # first item
1
>>> list1[2] # third item
hello

Negative numbers can be used to access items backwards:

>>> list1[-1] # last item
500

Nested lists

Lists can contain other lists, which can contain other lists, and so on. This is known as nested lists:

>>> list2 = [1, 2, 3, ["a", "b", "c"]]
>>> list3 = [1, 2, 3, ["a", ["A", "B"], "b", [], "c"]]

Items in nested lists can be accessed by ‘chaining’ the indexes of the parent lists and the item index:

>>> list3[-1][1][0]
'A'

Operations with lists

Lists can be added using the + operator. The result is a new list with the items of the lists concatenated in the given order.

>>> list1 + ['b', 100] + ['abcdef']
[1, -20, 'hello', 4.347, 500, 'b', 100, 'abcdef']

Lists can also be multiplied by integers. The result is equivalent to adding the list to itself n times:

>>> list1 * 3
[1, -20, 'hello', 4.347, 500, 1, -20, 'hello', 4.347, 500, 1, -20, 'hello', 4.347, 500]

Subtraction and division operations are not supported with lists.

Making copies of a list

Assigning a list to a new variable does not create another list, just a new reference to the same list object.

>>> anotherList = list1
>>> anotherList.append(-9999) # add an item to the list
>>> list1
[1, -20, 'asdsd', 4.347, 500, -9999]

We’ve added an item to anotherList, and it appeared when we printed list1. It makes sense once we understand that both variable names point to the same list.

There are different ways to create a copy of a list. One of them is using the built-in function list():

>>> aCopy = list(list1)
>>> aCopy.append(9999)
>>> aCopy
[1, -20, 'hello', 4.347, 500, -9999, 9999]
>>> list1
[1, -20, 'hello', 4.347, 500, -9999]

Another way of making a copy of a list is by creating a slice of the whole list (see the next section):

>>> anotherCopy = list1[:]

List slicing

A slice is a continuous range of items in a list. Slicing a list returns a new list.

Consider the following ASCII diagram

                +---+---+---+---+---+---+
                | P | y | t | h | o | n |
                +---+---+---+---+---+---+
Slice position: 0   1   2   3   4   5   6
Index position:   0   1   2   3   4   5

A slice is defined using start and end indexes separated by a colon:

>>> list1 = [1, -20, 'hello', 4.347, 500, 'abcdefg']
>>> list1[2:4] # get a slice from 3nd to 5th items
['hello', 4.347]

The item at the start index is included in the slice, the one at the end index is not.

To start a slice before the first item, use 0 or simply leave out the first index:

>>> list1[:4]   # same as: list1[0:4]
[1, -20, 'hello', 4.347]

To finish a slice after the last list item, leave out the second index:

>>> list1[2:]
['hello', 4.347, 500, 'abcdefg']]

Slice indexes can also be negative. We can count items from the end of the list

>>> list1[-2:]   # last two items in the list
[500, 'abcdefg']

or exclude some items at the end of the list

>>> list1[:-2]   # last two items in the list
[1, -20, 'hello', 4.347]   # everything except the last two items

The slicing notation works with any kind of sequence, so you can apply what you have learned here to strings and tuples

Adding items to a list

Use the append method to add new items to a list:

>>> list1 = ['spam', 'eggs']
>>> list1.append('bacon')
>>> list1
['spam', 'eggs', 'bacon']

Similarly, use the extend method to append a list of items to another list:

>>> list1.extend(['spam', 'spam'])
>>> list1
['spam', 'eggs', 'bacon', 'spam', 'spam']

The insert method allows you to insert an item at a specific position using a list index:

>>> list1.insert(3, 'sausages')
>>> list1
['spam', 'eggs', 'bacon', 'sausages', 'spam', 'spam']

Finally, the slice notation can be used to replace a section of a list with another list:

>>> list1[1:4] = ['spam', 'spam', 'spam']
>>> list1
['spam', 'spam', 'spam', 'spam', 'spam', 'spam']

Removing items from a list

List items can be removed using the del command and the item’s index:

>>> L = ['Graham', 'Eric', 'Terry', 'John', 'Terry', 'Michael']
>>> del L[-1]
>>> L
['Graham', 'Eric', 'Terry', 'John', 'Terry']

If you don’t know the index of item, you can use the remove method and refer to the item itself:

L.remove('Terry')
>>> L
['Graham', 'Eric', 'John', 'Terry']

If an item appears multiple times in the list, only the first one is removed.

The slice notation can be used to remove several continuous items at once:

>>> del L[1:3]
>>> L
['Graham', 'Terry']

‘Popping’ items

The pop method removes an item from a list and at the same time returns it. This is useful to make lists behave like stacks.

Here we take the last item from the stack:

>>> myList = ['parrot', 'ant', 'fish', 'goat', 'cat', 'rabbit', 'frog']
>>> myList.pop()
frog

If we ask to see the list again, we can see that the last item is gone:

>>> myList
['parrot', 'ant', 'fish', 'goat', 'cat', 'rabbit']

The pop method can also take a index – this allows us to take out an item which is not the last one:

>>> myList.pop(0) # take the first item
'parrot'
>>> myList
['ant', 'fish', 'goat', 'cat', 'rabbit']

Sorting lists

List items can be sorted using the sort method.

Sorting is straightforward when all items in a list are of the same type.

For example, if all items are strings, the list will be sorted alphabetically:

>>> stringsList = ['parrot', 'ant', 'fish', 'goat', 'cat', 'rabbit', 'frog']
>>> stringsList.sort()
>>> stringsList
['ant', 'cat', 'fish', 'frog', 'goat', 'parrot', 'rabbit']

If all items are numbers, they will be sorted numerically in ascending order:

>>> numbersList = [7, 3.1416, 13, -273.15, 2.718, 0, 356.25]
>>> numbersList.sort()
>>> numbersList
[-273.15, 0, 2.718, 3.1416, 7, 13, 356.25]

Reversing a list

The reverse method can be used to revert the order of the items in a list:

>>> aList = ['one', 'two', 'three', 'four', 'five']
>>> aList.reverse()
>>> aList
['five', 'four', 'three', 'two', 'one']

The sort method also has a reverse keyword to invert the default sorting order from ascending to descending:

# sort list items in reverse alphabetical order
>>> aList.sort(reverse=True)
>>> aList
['two', 'three', 'one', 'four', 'five']

Sorting lists with mixed object types

If a list contains different types of objects, we need to define a parameter for comparison. A straight sort with different object types is like comparing apples to oranges, and will not work in Python 3:

>>> mixedList = ['z', 'a', 'abcdefg', 100, 2.4, True, [], None]
>>> mixedList.sort()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'int' and 'str'

How does a number compare to a string? How do numbers and strings compare to lists, boolean values, or to None? There are different ways to answer these questions depending on the context and on the desired result.

Sorting mixed lists was supported in Python 2, but only thanks to many assumptions about comparisons between types. Python 3 requires us to be explicit about the comparison criteria used for sorting.

The example script shows how to sort a list containing different data types using a key function:

# create a flat list containing strings and numbers
myList = ['Graham', 'Eric', 'Terry', 'John', 'Terry', 'Michael']
myList += ['parrot', 'ant', 'fish', 'goat', 'cat', 'rabbit', 'frog']
myList += [7, 3.1416, 13, -273.15, 2.718, 0, 356.25]
myList += ['3M', '7UP', '7-Eleven']

# define a key function
def sorter(item):
    '''Convert a list item into a key for sorting.'''
    return str(item).lower()

# sort the list using the key function
myList.sort(key=sorter)

print(myList)
[-273.15, 0, 13, 2.718, 3.1416, 356.25, '3M', 7, '7-Eleven', '7UP', 'ant', 'cat', 'Eric', 'fish', 'frog', 'goat', 'Graham', 'John', 'Michael', 'parrot', 'rabbit', 'Terry', 'Terry']

Sorting lists of values with itemgetter

One of the many uses for lists is storing groups of values. As an example, let’s imagine that we have some very basic information about a group of people as a list of values: a tuple with first name, last name and birth year:

persons = [
    # first name, last name, birth year
    ('Graham', 'Chapman', 1941),
    ('Eric', 'Idle', 1943),
    ('Terry', 'Gilliam', 1940),
    ('Terry', 'Jones', 1942),
    ('John', 'Cleese', 1939),
    ('Michael', 'Palin', 1943),
]

Using what we’ve learned in the previous section, we could write separate key functions to sort this list based on different index values:

def lastNameSorter(item):
    return item[1]

def birthYearSorter(item):
    return item[2]

persons.sort(key=lastNameSorter) # option: key=birthYearSorter
print(persons)
[('Graham', 'Chapman', 1941), ('John', 'Cleese', 1939), ('Terry', 'Gilliam', 1940), ('Eric', 'Idle', 1943), ('Terry', 'Jones', 1942), ('Michael', 'Palin', 1943)]

Notice how the functions repeat a ‘get item’ pattern. This is so common that Python provides a convenience itemgetter function in the operator module (we’ll learn more about modules later):

from operator import itemgetter
persons.sort(key=itemgetter(2)) # sort by year
print(persons)
[('John', 'Cleese', 1939), ('Terry', 'Gilliam', 1940), ('Graham', 'Chapman', 1941), ('Terry', 'Jones', 1942), ('Eric', 'Idle', 1943), ('Michael', 'Palin', 1943)]

Using itemgetter it is also possible to define multiple levels of sorting. For example, sorting by first name (1st item) and last name (2nd item):

persons.sort(key=itemgetter(0, 1))
print(persons)
[('Eric', 'Idle', 1943), ('Graham', 'Chapman', 1941), ('John', 'Cleese', 1939), ('Michael', 'Palin', 1943), ('Terry', 'Gilliam', 1940), ('Terry', 'Jones', 1942)]

Using sorted() and reversed()

In addition to the list.sort method, Python also offers a sorted() built-in function which builds a new sorted list from a collection (not just a list). Using sorted(list) is often more convenient than using list.sort().

Here’s an example of sorted() in use. Notice that the original list is not modified:

>>> flags = ['Foxtrot', 'Bravo', 'Whisky', 'Tango', 'Charlie', 'Echo']
>>> sorted(flags)
['Bravo', 'Charlie', 'Echo', 'Foxtrot', 'Tango', 'Whisky']
>>> flags
['Foxtrot', 'Bravo', 'Whisky', 'Tango', 'Charlie', 'Echo']

The reversed() built-in function works similarly, but returns an iterator object instead of a new list.

Both sorted() and reversed() are often used to loop over a list of items:

>>> for flag in reversed(flags):
...     flag
'Echo'
'Charlie'
'Tango'
'Whisky'
'Bravo'
'Foxtrot'

Creating number sequences

Sequential lists of numbers can be created dynamically using the range built-in function.

Here’s how we create a list of numbers, starting at 0 and ending before 10:

>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In Python 2, range returns a list. In Python 3, it returns an iterator object – which we can convert into a list using the list() built-in function.

We can start a sequence at a different number by using range with two arguments:

>>> list(range(5, 10))
[5, 6, 7, 8, 9]

Finally, we can use a third argument to specify an interval between the numbers:

>>> list(range(1, 19, 3))
[1, 4, 7, 10, 13, 16]
Last edited on 01/09/2021