Lists in Python

Sequences

Sequences is a generic term used to refer to data types which can hold multiple items of data. In Python, we have several types of sequences, the following three are most important:

1. list
2. tuples
3. strings

In this chapter we will discuss list type.

What is list ? #

Suppose we want to calculate average marks of 100 students in a class. To accomplish this task, our first step would be to store marks of 100 students by creating 100 variables. What if we want to calculate average marks of 1000 students or more ? Should we create 1000 variables ? No ! Off course not. This way of solving problem would be very impractical. What we need is a list.

A list is sequences of multiple items separated by comma, and enclosed inside square brackets i.e `[]`. The syntax of creating list is as follows:

``````variable = [item1, item2, item3, ..., itemN]
``````

Each item that is stored in the list is called an element of list or simply an element. For example:

``````>>>
>>> numbers = [11, 99, 66, 22]
>>>
``````

This statement creates a list of 4 elements and assign it to the variable `numbers`. Note that just like everything else, a list is an object too, of type `list`. The `numbers` variable do not store the contents of the list, it only stores a reference to the address where list object is actually stored in the memory.

``````>>>
>>> type(numbers)
<class 'list'>
>>>
``````

To print the contents of a list in the Python shell just type the list name.

``````>>>
>>> numbers
[11, 99, 66, 22]
>>>
``````

We can also use `print()` function to print the list.

``````>>>
>>> print(numbers)
[11, 99, 66, 22]
>>>
``````

A list can contains elements of same or different types.

``````>>>
>>> mixed = ["a string", 3.14, 199]  # list where elements are of different types
>>>
>>> mixed
['a string', 3.14, 199]
>>>
``````

To create an empty list simply type square brackets without any elements inside it.

``````>>>
>>> empty_list = []  # an empty list
>>>
``````

We can also create list using the `list()` constructor function.

``````>>>
>>> list1 = list()  # an empty list
>>> list2 = list([3.2, 4, 0.12])  # again elements in a list can be of different types
>>> list3 = list(["@@", "###", ">>>"])   # you can use symbols too
>>> list4 = list("1234")  # creating list from string
>>>
>>>
>>> list1
[]
>>>
>>> list2
[3.2, 4, 0.12]
>>>
>>> list3
['@@', '###', '>>>']
>>>
>>> list4
['1', '2', '3', '4']
>>>
>>>
``````

The elements of a list can be a list too.

``````>>>
>>> list5 = [
...    [33, 55, 77],  # first element
...    [99, 31, 64]   # second element
... ]
>>>
>>>
>>> list5
[[33, 55, 77], [99, 31, 64]]
>>>
>>>
``````

`list5` contains two elements of type `list`. This type of lists is know as list of list or nested list or multi-dimensional list.

Creating list using range() function #

The `range()` function can also be used to create a long list. Recall that the range() function returns an object of type iterable, all we need to create a list is to pass this iterable object to the `list()` function, as follows:

``````>>>
>>> list1 = list(range(5))
>>> list1
[0, 1, 2, 3, 4]
>>>
>>>
``````

`range(5)` generates the following sequence:

``````0, 1, 2, 3, 4
``````

The `list()` function then uses numbers from this sequence to create a list.

Here are some more examples:

``````>>>
>>> list2 = list(range(1, 101))  ## create a list of numbers from 1 to 100
>>> list2
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
>>>
>>>
``````
``````>>>
>>> list3 = list(range(0, 100, 10))  ## create a list of numbers from 0 to 100 with step value of 10
>>> list3
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
>>>
>>>
``````

List functions #

The following table lists some functions, we commonly use while working with list

Function Description
`len()` returns the number of elements in a sequence.
`sum()` returns the sum of elements in sequence.
`max()` returns the element with greatest value in the sequence.
`min()` returns the element with smallest value in the sequence.
``````>>>
>>>
>>> list1 = [1, 9, 4, 12, 82]
>>>
>>> len(list1)  # find the length of the list
5
>>>
>>> sum(list1)  # find the sum of the list
108
>>>
>>> max(list1)  # find the greatest element in the list
82
>>>
>>> min(list1)  # find the smallest element in the list
1
>>>
>>>
``````

Index Operator #

Just like strings, the elements in a list are `0` indexed, meaning that the first element is at index `0`, second is at `1`, third is at `2` and so on. The last valid index will be one less than the length of the list. We use the following syntax to access an element from the list.

``````a_list[index]
``````

where `index` must be an integer.

For example, the following statement creates a list of 6 elements.

``````list1 = [88, 99, 4.12, 199, 993, 9999]
`
``````

Here `list1[0]` refers to the element `99`, `list1[1]` refers to `99` and `list[5]` refers to `9999`.

``````>>>
>>> list1 = [88, 99, 4.12, 199, 993, 9999]
>>>
>>> list1[0]  # get the first element
88
>>> list1[1]  # get the second element
99
>>> list1[5]  # get the sixth element
9999
>>>
``````

We can also calculate the last valid index of a list using the `len()` function, as follows:

``````>>>
>>> len(list1) - 1
5
>>>
>>> list1[len(list1) - 1]  # get the last element
9999
>>>
``````

Trying to access a element beyond the last last valid index will result in `IndexError`.

``````>>>
>>> list1[100]   # get the element at index 100
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>>
>>>
``````

Just like strings, negative indexes are still valid here. In fact we can use can negative index on almost all types of sequences in Python.

As you can see from the image the last element is at index `-1`, the second last element is at `-2` and so on. The first element is at index `-6`.

``````>>>
>>> list1
[88, 99, 4.12, 199, 993, 9999]
>>>
>>> list1[-1]   # get the last element
9999
>>> list1[-2]   # get the second last element
993
>>>
``````

To calculate the index of the first element use `len()` function as follows:

``````>>>
>>> list1[-len(list1)]  # get the first element
88
>>>
``````

Lists are Mutable #

List are mutable, what this means is that we can modify a list without creating a new list in the process. Consider the following example:

``````>>>
>>> list1 = ["str", "list", "int", "float"]
>>>
>>> id(list1)   # address where list1 is stored
43223176
>>>
>>> list1[0] = "string"  # Update element at index 0
>>>
>>> list1   # list is changed now
['string', 'list', 'int', 'float']
>>>
>>> id(list1)  # notice that id is still the same
43223176
>>>
>>>
``````

Notice that even after modifying the list the id of the variable `list1` remains the same. This indicates that no new list object is created when we assign new element at index `1`.

Iterating through Elements in a List #

To iterate though a list we can use for loop as follows:

``````>>>
>>> marks = [122, 45, 23, 78, 65, 12]
>>> for m in marks:
...   print(m)
...
122
45
23
78
65
12
>>>
``````

In each iteration variable `m` is assigned a value from the list. Changing the value of variable `m` in the loop body doesn't update the elements in the list. So, this way is commonly used to iterate over list when we don't need modify the elements in a list.

To modify elements, we use for loop in conjunction with `range()` function, as follows:

``````>>>
>>> marks = [122, 45, 23, 78, 65, 12]
>>>
>>> import random
>>>
>>> for i in range(len(marks)):
...   marks[i] = random.randint(1, 100)  # assign some random value between 1 to 100 to all elements
...
>>>
>>> marks
[59, 9, 59, 21, 75, 61]
>>>
>>>
``````

Although, the for loop is a preferred way to iterate over list, if we want we can use while loop too. For example:

``````>>>
>>> i = 0
>>>
>>> while i < len(marks):
...   print(marks[i])
...   i += 1
...
59
9
59
21
75
61
>>>
>>>
``````

List Slicing #

Slicing operator we discussed in lesson Strins in Python is also available in list. The only difference is instead of returning a slice of string, here it returns a slice of list. It's is syntax is:

``````list[start:end]
``````

This returns a slice of list starting from index start to `end - 1`. Here are some examples:

``````>>>
>>> list1 = [11, 33, 55, 22, 44, 89]
>>>
>>> list1[0:4]
[11, 33, 55, 22]
>>>
>>>
>>> list1[1:5]
[33, 55, 22, 44]
>>>
>>>
>>> list1[4:5]
[44]
>>>
``````

The `start` and `end` indexes are optional, if not specified then start index is `0` and end index is `length` of the list.

``````>>>
>>> list1
[11, 33, 55, 22, 44, 89]
>>>
>>> list1[:2]       # same as list1[0:2]
[11, 33]
>>>
>>> list1[2:]       # same as list1[2:len(list1)]
[55, 22, 44, 89]
>>>
>>> list1[:]        # same as list1[0:len(list1)]
[11, 33, 55, 22, 44, 89]
>>>
>>>
``````

Membership operator in and not in #

Just like strings, in list we can check whether an element exists in the list or not using `in` and `not in` operator. Here are some examples:

``````>>>
>>> cards  = ["club", "diamond", "heart", "spades"]
>>>
>>> "club" in cards
True
>>>
>>> "joker" in cards
False
>>>
>>> "pikes" not in cards
True
>>>
>>> "heart" not in cards
False
>>>
>>>
``````

List Concatenation #

List can be joined too using `+` operator. When operands on both side are lists `+` operator creates a new list by combing elements from both the lists.

``````>>>
>>>
>>> list1 = [1,2,3]  # create list1
>>> list2 = [11,22,33]  # create list2
>>>
>>> id(list1)   # address of list1
43223112
>>> id(list2)   # address of list2
43223048
>>>
>>>
``````
``````>>>
>>> list3 = list1 + list2   # concatenate list1 and list2 and create list3
>>> list3
[1, 2, 3, 11, 22, 33]
>>>
>>>
``````
``````>>>
>>> id(list3)  # address of the new list list3
43222920
>>>
>>>
>>> id(list1)   # address of list1 is still same
43223112
>>> id(list2)   # address of list2 is still same
43223048
>>>
>>>
``````

Notice that concatenation doesn't affect the `list1` and `list2`, their addresses remains the same before and after the concatenation.

Another way to concatenate list is use `+=` operator. Here is an example:

``````>>>
>>> list1
[1, 2, 3]
>>>
>>> id(list1)
43223112
>>>
>>> list1 += list2   # append list2 to list1
>>>
>>> list1
[1, 2, 3, 11, 22, 33]
>>>
>>>
>>> id(list1)  # address is still same
43223112
>>>
``````

The statement `list1 += list2` appends `list2` to the end of `list1`. Notice that address of `list1` is still not changed. Unlike languages like C, C++ Java, where arrays are of fixed size. In Python, lists are dynamically sized. It means that size of list grows automatically as needed.

Repeatition Operator #

We can use `*` operator with lists too. It's syntax is:

``````sequence * n
``````

The `*` operator replicates the list and then joins them. Here are some examples:

``````>>>
>>> list1 = [1, 5]
>>>
>>>
>>> list2 = list1 * 4  # replicate list1 4 times and assign the result to list2
>>>
>>> list2
[1, 5, 1, 5, 1, 5, 1, 5]
>>>
>>>
``````

The `*` operator can also be used as a compound assignment operator `*=` . The only difference is that instead of creating a new list object, it would update the existing list object.

``````>>>
>>> action = ["eat", "sleep", "repeat"]
>>>
>>> id(action)  # address of action list
32182472
>>>
>>> action *= 5
>>>
>>> action
['eat', 'sleep', 'repeat', 'eat', 'sleep', 'repeat', 'eat', 'sleep', 'repeat', '
eat', 'sleep', 'repeat', 'eat', 'sleep', 'repeat']
>>>
>>> id(action)   # address is still the same
32182472
>>>
``````

Comparing Lists #

Just like strings, we can compare lists using relational operators (`>`, `>=`, `<`, `<=`, `!=`, `==`). List comparison only works when operands involved contains the same type of elements. The process starts off by comparing the elements at index `0` from both list. The comparison stops only when either the end of the list is reached or corresponding characters in the list are not same.

Consider the following example:

``````>>>
>>> n1 = [1,2,3]
>>> n2 = [1,2, 10]
>>>
>>>
>>> n1 > n2
False
>>>
``````

Here are the steps involved in the comparison list `n1` and `n2`.

Step 1: `1` from `n1` is compared with `1` from `n2`. As they are same, the next two characters are compared.

Step 2: `2` from `n2` is compared with `2` from `n2`. Again they are same, the next two characters are compared.

Step 3: `3` from `n1` is compared with `10` from `10`. Clearly `3` is smaller than `10`. So the comparision `n1 > n2` returns false.

Here is another example where elements are strings.

``````>>>
>>> word_list1 = ["pow", "exp"]
>>> word_list2 = ["power", "exponent"]
>>>
>>> word_list1 < word_list2
True
>>>
``````

Step 1: `"pow"` from `word_list1` is compared with `"power"` from `word_list2`. Clearly `"pow"` is smaller than `"power"`. At this point, comparison stops because we have found the elements in list which are not same , so comparison `word_list1 < word_list2` is true.

List Comprehension #

Often times you will need to create lists where each element in the sequence is a result of some operations or each element in the list satisfies some condition. For example, create a sequence of cubes of numbers from `50` to `100`. We use list comprehension in situations like these. The syntax of list comprehension is:

``````[ expression for item in iterable ]
``````

here is how it works:

In each iteration item is assigned a value from iterable object, then the expression before the for keyword is evaluated; the result of this expression is then used to produce values for the list. The process repeats until there are no more elements left to iterate.

Here is an example:

``````>>>
>>> cube_list = [ i**3 for i in range(50, 101) ]
>>>
>>> cube_list
[125000, 132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379,
216000, 226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509,
343000, 357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039,
512000, 531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969,
729000, 753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299,
1000000]
>>>
``````

We can also include an if condition in the list comprehension, as follows

``````[ expression for item in iterable if condition ]
``````

This works exactly like above the only difference is that the expression before the for keyword is only evaluated when condition is `True`.

Here is an example:

``````>>>
>>> even_list = [ i for i in range(1, 10) if i % 2 == 0 ]
>>>
>>> even_list
[2, 4, 6, 8]
>>>
``````

List Methods #

The `list` class have many built-in methods which allows us add element, remove element, update element and much more. The following table lists some common methods provided by `list` class to manipulate lists.

Method Description
`appends(item)` adds an `item` to the end of the list.
`insert(index, item)` inserts an `item` at the specified `index`. If `index` specified is greater than the last valid `index`, `item` is added to the end of the list.
`index(item)` returns the index of the first occurrence of specified `item`. If the specified `item` doesn't exists in the list, an exception is raised.
`remove(item)` removes the first occurrence of the specified `item` from the list. If the specified `item` doesn't exists in the list, an exception is raised.
`count(item)` returns number of times an `item` appears in the list.
`clear()` removes all the element from the list.
`sort()` sorts the list in ascending order.
`reverse()` reverse the order of elements in the list.
`extends(sequence)` appends the elements of the sequence to the list.
`pop([index])` removes the element at the specified index and returns that element. If index is not specified, it removes and returns last element from the list. When index is not valid, an exception is raised.

Note that all these method except `count()` modify the list object on which it is called.

The following shell session demonstrates how to use these methods:

append() method #

``````>>>
>>> list1 = [1,2,3,4,5,6]
>>>
>>> id(list1)
45741512        # address of list1
>>>
>>> list1.append(10)   # append 10 to list1
>>>
>>> list1
[1, 2, 3, 4, 5, 6, 10]
>>>
>>> id(list1)   # address remains unchanged
45741512
>>>
``````

insert() method #

``````>>>
>>> list1 = [1,2,3,4,5,6]
>>>
>>> id(list1)
45739272
>>>
>>> list1.insert(2, 1000)  # insert item 1000 at index 2
>>>
>>> list1
[1, 2, 1000, 3, 4, 5, 6]   # now the last valid index is 6
>>>
>>>
>>> list1.insert(6, 4000)  # insert the item 4000 at index 6
>>>
>>> list1
[1, 2, 1000, 3, 4, 5, 4000, 6]  # now the last valid index is 7
>>>
>>>
>>>
>>> list1.insert(8, 8000)  # insert the item 8000 at index 8, which is beyond the last valid index
>>>
>>> list1
[1, 2, 1000, 3, 4, 5, 4000, 6, 8000]
>>> list1[8]
8000
>>>
``````

index() method #

``````>>>
>>>
>>> list1 = [1, 2, 3, 4, 5, 6, 1, 2, 3]
>>>
>>> list1.index(1)  # index of first occurrence of element 1
0
>>> list1.index(3)  # index of first occurrence of element 3
2
>>> list1.index(90)  # an exception is raised, as value 90 doesn't exists in the list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 90 is not in list
>>>
>>>
``````

remove() method #

``````>>>
>>> list1 = [1, 2, 3, 4, 5, 6, 1, 2, 3]
>>>
>>> list1.remove(1)   # remove first occurence of element 1
>>>
>>> list1
[2, 3, 4, 5, 6, 1, 2, 3]
>>>
>>> list1.remove(2)   # remove first occurence of element 2
>>>
>>> list1
[3, 4, 5, 6, 1, 2, 3]
>>>
>>> list1.remove(100)  # an exception is raised, as value 100 doesn't exists in the list
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
>>>
>>>
``````

count() method #

``````>>>
>>> list1 = [1, 2, 3, 4, 5, 6, 1, 2, 3]
>>>
>>> list1.count(2)   # count the appearance of element 2 in the list
2
>>> list1.count(100)  # count the appearance of element 100 in the list
0
>>> list1.count(1)  # count the appearance of element 1 in the list
2
>>>
``````

clear() method #

``````>>>
>>>
>>> list1 = [1, 2, 3, 4, 5, 6, 1, 2, 3]
>>>
>>> id(list1)
45738248
>>>
>>> list1.clear()   # clear all the elements in the list list1
>>>
>>> list1
[]
>>>
>>> id(list1)
45738248
>>>
>>>
``````

sort() method #

``````>>>
>>>
>>> list1 = [12, -2, 3, 4, 100, 50]
>>>
>>> list1.sort()   # sort the list in ascending order
>>>
>>> list1
[-2, 3, 4, 12, 50, 100]
>>>
>>> types = ["str", "float", "int", "list"]
>>>
>>> types.sort()  # sort the list, strings are sorted based on the ASCII values
>>>
>>> types
['float', 'int', 'list', 'str']
>>>
>>>
``````

reverse() method #

``````>>>
>>>
>>> list1 = [12, -2, 3, 4, 100, 50]
>>>
>>>
>>> list1.sort()  # sort the list in ascending order
>>>
>>> list1
[-2, 3, 4, 12, 50, 100]
>>>
>>> list1.reverse()  # sort the list in descending order
>>>
>>> list1
[100, 50, 12, 4, 3, -2]
>>>
>>>
``````

extends() method #

``````>>>
>>> list1 = [1, 2, 3]
>>>
>>> id(list1)
45738248
>>>
>>> list1.extend([100, 200, 300])   # append elements of list [100, 200, 300] to list1
>>>
>>> list1
[1, 2, 3, 100, 200, 300]
>>>
>>> id(list1)
45738248
>>>
>>>
>>> list1.extend("str")  # We can pass strings too
>>>
>>> list1
[1, 2, 3, 100, 200, 300, 's', 't', 'r']
>>>
>>>
>>> id(list1)
45738248
>>>
>>>
``````

pop() method #

``````>>>
>>> list1 = [1, 2, 3, 4, 5, 6]
>>>
>>> list1.pop(4)  # remove the element at index 4
5
>>> list1
[1, 2, 3, 4, 6]
>>>
>>> list1.pop()   # remove the last element
6
>>>
>>> list1
[1, 2, 3, 4]
>>>
>>>
>>> list1.pop(10)  # index specified is not valid
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: pop index out of range
>>>
>>>
``````