## Introduction

The list is one of the most important data structures available in Python used to store multiple items in a single variable. It is written as a sequence of comma-separated values enclosed within a pair of square brackets [ ]. Note that the items in a list need not be of the same type.

EXAMPLE Definiting a list.

1listA = [1, 2, 3, 4, 5, 6]
2listB = ["a", "b", 'c', "d",'f']
3listC = ['UK', 'USA', True, 2021, 2022]
4print(listA, listB,listC)
5print(type(listA), type(listB), type(listC))

[1, 2, 3, 4, 5, 6] ['a', 'b', 'c', 'd', 'f'] ['UK', 'USA', True, 2021, 2022]
<class 'list'> <class 'list'> <class 'list'>


In the above example, the first 2 lists store homogeneous data (values have the same type) whereas the last one stores heterogeneous data (values of different types).

## Basic List Operations

Just like strings, lists can also be concatenated with the + operator.

EXAMPLE Concatenation of lists.

1listA+listB

[1, 2, 3, 4, 5, 6, 'a', 'b', 'c', 'd', 'f']


We can obtain the length of a list using the len() function.

EXAMPLE Length of a list.

1len(listA)

6


EXAMPLE Repetition using the * operator.

1listB*2

['a', 'b', 'c', 'd', 'f', 'a', 'b', 'c', 'd', 'f']


The in keyword serves two purposes:

• used to check if a value is present in a sequence (e.g. list).
• used to iterate through a sequence in a for loop.

EXAMPLE Boolean expression with a list.

12022 in listC # Is 2022 an element of listC?

True


A nested list is a list that contains other lists as its elements.

EXAMPLE Nested list.

1newlist = [listA, listB]
2newlist

[[1, 2, 3, 4, 5, 6], ['a', 'b', 'c', 'd', 'f']]


## List Indexing and Slicing

Indexing and slicing for lists are similar to those for strings .

EXAMPLE List indexing.

1listA

1


EXAMPLE Negative indexing (count from right).

1listA[-2]

5


EXAMPLE List slicing.

1listA[2:]

[3, 4, 5, 6]


Chain indexing and slicing work for a nested list.

EXAMPLE

1newlist # first sub-list of a nested list

[1, 2, 3, 4, 5, 6]


EXAMPLE

1newlist[2:4]

['c', 'd']


EXAMPLE Matrix: a nested list.

1row1 = [1,2,3]
2row2 = [4,5,6]
3row3 = [7,8,9]
4mat = [row1, row2, row3]
5mat

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Tip: We can print out individual elements of a list by putting an asterisk * in front of the list in a print() statement.

EXAMPLE Printing individual elements of a list.

1print(*listA)

1 2 3 4 5 6


If we want to print out the elements of a list line by line, we can use the \n (new line character) as the sep argument.

EXAMPLE Printing individual elements of a list (line by line).

1print(*listA, sep='\n' )

1
2
3
4
5
6


Combining what we have learnt above, we can print out a matrix, row by row, using:

EXAMPLE Printing a nested list in matrix form.

1print(*mat, sep='\n')

[1, 2, 3]
[4, 5, 6]
[7, 8, 9]


EXAMPLE Chain indexing of a nested list.

1mat # first row, second element

2

Note: The above procedure can be used to define a matrix but we will be using the more specialized NumPy arrays for that purpose.

## Mutable vs Immutable Objects

It is appropriate at this juncture to digress into a discussion on mutability and immutability of Python objects. All the data in Python is represented by objects (or by relations between objects). Every object has an identity, a type, and a value. So far, all the basic data types in Python we have studied are immutable. Integers (int), floats (float) and strings (str) are all immutable.

For example, in the following, the object represented by variable a contains data “Singapore” and has type str. It has an identity which can be found using the function id() which returns an integer representing its address in memory.

EXAMPLE Identity of an object using id().

1a="Singapore"
2print(a)
3print(type(a))
4print(id(a))

Singapore
<class 'str'>
2269554790960


If we try to modify the object by appending “an”, the identity changes which means the variable a no longer refers to the original object. In other words, the original object (with the original id above) hasn’t changed at all since strings are immutable. A new object (with a new id) is created when we append “an” to a and is now referenced by the same variable a. The variable a no longer refers to the original object.

EXAMPLE Strings are immutable.

1a = a + "an"
2print(a)
3print(id(a))

Singaporean
2269554498992


On the other hand, lists are mutable as demostrated in the following example.

EXAMPLE Identity of a list object.

1myList = ['foo','bar','cup']
2print(myList)
3print(id(myList))

['foo', 'bar', 'cup']
2269554819584


We now modify the second element of the list.

EXAMPLE Lists are mutable.

1myList = 'hat'
2print(myList)
3print(id(myList))

['foo', 'hat', 'cup']
2269554819584


We realize the identity of the list object remains unchanged after the modification. We say that list has been modified in place. This shows that lists are mutable Python objects.

As a second example, we create a list object and refer to it using 2 different variable names.

EXAMPLE A list object with two variable names.

1values = [1, 2, 3]
2values2 = values
3print(id(values))
4print(id(values2))

2269554820608
2269554820608


We now modify the second list object (values2) by making an appendment. Since lists are mutable, the object is modified in place and its identity remains unchanged.

EXAMPLE Modifying a mutable object with two variable names.

1values2.append(4)
2print(values)
3print(values2)
4print(id(values))
5print(id(values2))

[1, 2, 3, 4]
[1, 2, 3, 4]
2269554820608
2269554820608


Since we have used two variable names to refer to the same mutable object whose identity remains unchanged, it isn’t surprising to observe that the variable names continue to have the same identity.

Note: If a list object is referred to by two variable names, when we change the value of one variable, the value of the other variable is also changed automatically. This happens only for mutable objects.

Let’s try to repeat the above exercise with an immutable string object.

EXAMPLE A string object with two variable names.

1text = "Singapore"
2text2 = text
3print(text)
4print(text2)
5print(id(text))
6print(id(text2))

Singapore
Singapore
2269554830000
2269554830000


Both text and text2 point to the same string object and therefore have the same identity. We now modify the string object by making an appendment. Since strings are immutable, a new object is created and its identity changes.

EXAMPLE Modifying an immutable object with two variable names.

1text = text+ "an"
2print(text)
3print(text2)
4print(id(text))
5print(id(text2))

Singaporean
Singapore
2269554828336
2269554830000


When Python executes the first statement, a new object (with a new id) is created and is referenced by the same variable name text which no longer refers to the original object. Therefore, the identity changes. That is, id(text) is not the same as the previous id(text). However, variable text2 still refers to the original object (which hasn’t been modified). Therefore, both value and identity of text2 are the same as the previous text2.

## List Methods

Python has a set of built-in methods that we can use on lists. Let’s first create a list object.

1fruits = ["apple", "banana", "orange"]
2fruits

['apple', 'banana', 'orange']


The append() method appends an element to the end of the list.

EXAMPLE append() method.

1fruits.append('pineapple')
2fruits

['apple', 'banana', 'orange', 'pineapple']


Note that the method append() has modified the original object (identity remains unchanged).

The sort() method sorts the list alphabetically in ascending order by default.

EXAMPLE sort(): ascending order.

1cats =["Jaguar", "Lion", "Puma", "Leopard", "Panther", "Cheetah", "Tiger" ]
2cats.sort()
3cats

['Cheetah', 'Jaguar', 'Leopard', 'Lion', 'Panther', 'Puma', 'Tiger']


To sort the list alphabetically in descending order, we set the “reverse” parameter as True.

EXAMPLE sort(): descending order.

1cats.sort(reverse=True)
2cats

['Tiger', 'Puma', 'Panther', 'Lion', 'Leopard', 'Jaguar', 'Cheetah']


By default, the sort() method sorts the list in ascending order.

EXAMPLE sort(): ascending order.

1numbers =[8,2,7,3,5,12,3]
2numbers.sort()
3numbers

[2, 3, 3, 5, 7, 8, 12]


Or sorting the numbers in descending order.

EXAMPLE sort(): descending order.

1numbers.sort(reverse=True)
2numbers

[12, 8, 7, 5, 3, 3, 2]


The extend() method extends an existing list by appending more elements. This is in contrast to the append() method that appends only one element at a time.

EXAMPLE extend() method.

1students =["Jane", "Tom", "Bruce"]
2more_students = ["Janet", "Wendy", "James"]
3students.extend(more_students)
4students

['Jane', 'Tom', 'Bruce', 'Janet', 'Wendy', 'James']


Or recall that we can simply concatenate the 2 lists with the + operator

EXAMPLE Concatenation with +.

1students =["Jane", "Tom", "Bruce"]
2students + more_students

['Jane', 'Tom', 'Bruce', 'Janet', 'Wendy', 'James']


The insert() method inserts an element at a specified index location.

EXAMPLE insert() method.

1fruits = ['apple', 'banana', 'orange', 'pineapple']
2fruits.insert(2,'pear') # inserts at index=2
3fruits

['apple', 'banana', 'pear', 'orange', 'pineapple']


The pop() method removes an element at a specified index location.

EXAMPLE pop() method.

1fruits.pop(3) # removes element at index=3
2fruits

['apple', 'banana', 'pear', 'pineapple']


The count() method returns the number of elements with the specified value.

EXAMPLE count() method.

1numbers = [1,2,2,2,3,3,4,5,6,6,8]
2numbers.count(3) # counts how many times the number 3 appears

2


The index() method searches the list for a specified value and returns its index.

EXAMPLE index() method.

1numbers.index(5)

7


In cases where there is more than one occurrence of the specified value, the index() method will only return the first index.

EXAMPLE index() method returns only the index of the first occurrence.

1numbers.index(2)

1


What if we want to have the indices of all occurrences of the specified value? We will discuss this case when we learn about control flow and list comprehension in a later section.

The copy() method returns an exact copy of the list.

EXAMPLE copy() method.

1a=[1,2,3]
2b=a.copy()
3b

[1, 2, 3]


But why not just use b=a to duplicate a?

Answer: recall that lists are mutable. There are consequences in using the expression b=a. This means that whatever happens to a will happen to b since they refer to the same list object. If you only want to create an independent duplicate of a, the copy() method is more appropriate. The following example further illustrates this point.

EXAMPLE copy() method and mutability of a list object.

Scenario 1: b is created from b=a.copy(). Whatever happens to a will not affect b.

1a=[1,2,3]
2b=a.copy()
3a = 10
4b

[1, 2, 3]


Scenario 2: b is created from b=a. Whatever happens to a will impact b.

1a=[1,2,3]
2b=a
3a = 10
4b

[10, 2, 3]


The following table summarizes the Python list methods we have discussed so far:

Method Description
append() Adds an element at the end of the list.
copy() Returns a copy of the list.
count() Returns the number of elements with the specified value.
clear() Removes all the elements from the list.
extend() Add the elements of a list to the end of the current list.
index() Returns the index of the first element with the specified value.
insert() Adds an element at the specified position.
pop() Removes the element at the specified position.
remove() Removes the first item with the specified value.
reverse() Reverses the order of the list.
sort() Sorts the list.