⇦ Back

1 Introduction to Lists

As introduced in the data types page, ‘lists’ are collections of objects:

  • They have square brackets at the start and end of them and commas separating each item, eg [0, 1, 2, 3, 4]
    • This makes them easy to spot in Python - if you see square brackets and commas you’re looking at a list!
  • The proper word to use when referring to the things/objects/items in a list is elements - a list is made up of elements
  • A list’s elements do not all have to be of the same type, there can be a mixture of data types, eg [0, 1.2, 'two', True, None] contains an integer, a float, a string, a Boolean and a null object
  • A list can contain duplicates, eg [1, 2, 2, 3, 3, 3]
  • A list has an order: [0, 1, 2] is different to [0, 2, 1]
  • The elements are numbered in their order, starting with 0: the list ['A', 'B', 'C'] has 'A' as its zeroth element, 'B' as its first element and 'C' as its second element
    • The elements are also numbered backwards: “-1” refers to the last element, “-2” refers to the second last element and so on
    • The number associated with an element is its index, eg in the list ['A', 'B', 'C'] the element 'A' has index 0, 'B' has index 1 and 'C' has index 2
  • A list can be of any length, and can also be empty: [] is an empty list (it’s a list with zero elements in it), but it’s still a list
    • In other words, [] is not the same as [None] which is a list with a null object in it (which thus has a length of one - not zero - because it has one object in it!)

2 Creating Lists

As mentioned above, a list is created by using square brackets and commas between elements. This then needs to be assigned to a variable name using “=” in order to be used:

ls = ['zero', 'one', 'two', 3, 4, 5]
print(ls)
## ['zero', 'one', 'two', 3, 4, 5]

Note that you cannot use the word “list” as a variable name (we used “ls” in the example above). This is because “list” is the name of a function in Python: it’s a function that creates a list out of another object. Here, then, is how to use this list function to create a list:

natural_numbers = list(range(15))
print(natural_numbers)
## [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
counting_numbers = list(range(1, 21))
print(counting_numbers)
## [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
every_second_number = list(range(1, 19, 2))
print(every_second_number)
## [1, 3, 5, 7, 9, 11, 13, 15, 17]

In the above examples, the list() function is used to convert objects of the range type into objects that are of the list type.

You can find out the length of a list (ie how many elements are in it) but using the len() function:

ls = ['A', 'B', 'C', 'D', 'E']
print(len(ls))
## 5

3 Indexing Lists

You can access a single, specific element of a list by referring to its index. This is known as indexing a list and is done by placing the index of the element you are interested in inside of square brackets immediately after the variable name:

ls = ['A', 'B', 'C', 'D', 'E']
print(ls[2])
## C

You can access multiple elements by using a slice. With a slice, you tell Python where within a list it should start giving you elements and where it should stop. This is done by specifying a start index and a stop index, separated by a colon:

ls = ['A', 'B', 'C', 'D', 'E']
print(ls[1:4])
## ['B', 'C', 'D']

Note that, in the above example, we sliced the list from index 1 to index 4. In other words, we were left with the elements 1, 2 and 3; the element associated with the second index number we provide is NOT included in the output. A slice [a:b] will give you the elements from a to b including a but not including b - Python starts at index a and stops at index b. This also works when you use negative numbers as the indexes:

ls = ['A', 'B', 'C', 'D', 'E']
print(ls[-4:-1])
## ['B', 'C', 'D']

Also note that when you index a list with a single integer you get that elements on its own, whereas when you index using a slice you get a list, even if that slice only gives you one element:

# The element at index 3, on its own
print(ls[3])
## D
# The element at index 3, in a list
print(ls[3:4])
## ['D']

By default, a slice will start at the beginning of a list and end at the end. This means that if you don’t provide a start and/or stop index it will assume you want all the elements. This means that the slice [:] is the same as slice [0:len(ls)] (where len(ls) is the length of the list) in that both will return the entire list:

ls = ['A', 'B', 'C', 'D', 'E']
print(ls[:])
## ['A', 'B', 'C', 'D', 'E']
print(ls[0:len(ls)])
## ['A', 'B', 'C', 'D', 'E']

Similarly, you can leave out a start or stop index to get all elements to or from a certain index, respectively:

ls = ['A', 'B', 'C', 'D', 'E']
print(ls[2:])
## ['C', 'D', 'E']
print(ls[:-3])
## ['A', 'B']

If you include a third number in a slice, that will be its ‘stride’ (how many elements it will skip between each element it returns):

ls = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
# Every second element from the second to the eighth
print(ls[2:9:2])
## ['C', 'E', 'G', 'I']

A negative stride goes through the list backwards:

# Every second element from the eighth to the second
print(ls[8:1:-2])
## ['I', 'G', 'E', 'C']

Note that an integer or a slice can be used to index a list but a list cannot be used to index a list. In other words, ls[1, 2, 3] will return an error.

4 Finding in Lists

The opposite to indexing a list (whereby you provide an index number and get the corresponding element returned to you) is finding in a list (whereby you provide an element and get the corresponding index returned to you). You can lookup an element’s index in this way by using the the .index() method:

ls = ['A', 'B', 'C', 'D', 'E']
print(ls.index('B'))
## 1

Note that you can only lookup one element at a time in this way. Also, if there are multiple instances of the same element you will only get the index of the first one. To do more advanced finding you will need to use either a list comprehension or a for loop depending on what exactly you want to do with the output (see the page on list comprehensions for more).

4.1 Searching in Lists

If you are only interested in knowing if a certain value exists as an element in a list or not, as opposed to where that element is, you can search for it using the in statement and get a Boolean (True or False) as an output:

ls = ['A', 'B', 'C', 'D', 'E']
print('B' in ls)
## True
print('F' in ls)
## False

5 More Advanced Finding

The next() function can be used as a more powerful alternative to indexing/finding/searching a list. This function looks at each element in a list in turn, starting at the beginning, and for each one determines if it matches a given criterion. The first element it finds that does so will be returned in the manner of your asking. For example, you can ask the function to return the “next value, out of all the values in the list, where the value is equal to ‘B’”. This looks as follows:

ls = ['A', 'B', 'C', 'D', 'E']
print(next(value for value in ls if value == 'B'))
## B

Alternatively, if your list is made up of numbers, you can ask the function to return the “next value, out of all the values in the list, if the value is greater than 5”:

ls = [2, 4, 6, 8, 10]
print(next(value for value in ls if value > 5))
## 6

In this case, the first value that matched was “6”, which is what was returned. You also have the option to ask the function to return the element to you in an augmented manner, eg as a string:

ls = [2, 4, 6, 8, 10]
print(next(f'The next number >5 is: {value}' for value in ls if value > 5))
## The next number >5 is: 6

It is also possible to get the index of the element that first matches your criterion as opposed to the value. This requires the enumerate() function, which splits each element in a list up into its index and its value. As such, you need to ask for the “next index, given each index and value in the list, if the value is greater than 5”:

ls = [2, 4, 6, 8, 10]
print(next(index for index, value in enumerate(ls) if value > 5))
## 2

The element at index position 2 (which has the value “6”) was the first to match the criterion of being greater than 5.

As the name suggests, the next() function only finds the next element that meets the given criterion and, as such, can only ever return one element (or zero elements if there are none that meet the criterion). If you are instead interested in finding all the elements in the list you will need to use a list comprehension .

6 Concatenating Lists

Adding two lists together is very easy in Python: just use the “+” sign!

ls_1 = ['A', 'B', 'C']
ls_2 = ['D', 'E']
ls = ls_1 + ls_2
print(ls)
## ['A', 'B', 'C', 'D', 'E']

This happens even if the lists’ contents are numbers:

ls_1 = [1, 2, 3]
ls_2 = [4, 5]
ls = ls_1 + ls_2
print(ls)
## [1, 2, 3, 4, 5]

If you want to add the elements of the lists together you need to use a list comprehension (or convert the lists to arrays and add those together, in which case it will perform matrix addition). Those topics are covered on different pages but, in the meantime, here’s how to add specific elements of lists together using the “+” sign for addition and then append the answer onto the end of a string using the “+” sign for concatenation:

ls_1 = [1, 2, 3]
ls_2 = [4, 5]
print('Adding the numbers at indexes 0 and 1 = ' + str(ls_1[0] + ls_2[1]))
## Adding the numbers at indexes 0 and 1 = 6

While concatenating two lists together is possible using the “+” sign, it is not possible to remove the values in one list from another list using a “-” sign! It is, however, possible to do this with sets, which are discussed below.

6.1 Adding to Lists

If you just want to add single elements to lists as opposed to concatenating entire lists together, you can use the .append() or .insert() methods:

ls = [1, 2, 3]
# Add an element to the end of the list
ls.append(4)
print(ls)
## [1, 2, 3, 4]
ls = [1, 2, 4]
# At the given index, insert the given element
ls.insert(2, 3)
print(ls)
## [1, 2, 3, 4]

Note that these two operations occur in-place: they augment the list immediately and don’t return anything:

ls = [1, 2, 3]
# This is NOT what you want
ls = ls.append(4)
print(ls)
## None
ls = [1, 2, 4]
# This is NOT what you want
ls = ls.insert(2, 3)
print(ls)
## None

Also note that both these operations can only add one element at a time.

7 Removing from Lists

You can either remove elements from lists by specifying which indexes to take out or by specifying which values to take out:

7.1 Specifying Indexes

The first option is to just use indexing: use an integer or slice to return a subset of the original list:

ls = ['A', 'B', 'C', 'D', 'E']
subset = ls[1:-1]
print(subset)
## ['B', 'C', 'D']

The second option is to use the del statement (short for “delete”). This is the inverse of indexing: you use an integer or slice to specify which indexes to remove instead of which to keep as we have been doing up until now:

ls = ['A', 'B', 'C', 'D', 'E']
del ls[1:-1]
print(ls)
## ['A', 'E']

Note that this is another in-place operation: the elements are deleted from the list and no new list is created.

The third option is the .pop() method which removes the element at the given index from the list in-place and returns the element that was removed (or ‘popped-out’ as it were):

ls = ['A', 'B', 'C', 'D', 'E']
removed = ls.pop(2)
print(removed)
## C
print(ls)
## ['A', 'B', 'D', 'E']

7.2 Specifying Values

To remove a value from a list, use the .remove() method:

ls = ['A', 'B', 'C', 'B', 'A']
ls.remove('B')
print(ls)
## ['A', 'C', 'B', 'A']

Again, this has acted in-place and only on the first occurrence of the value. To remove multiple values from a list at a time you could either use a set or a list comprehension. See the section below for how to do this with sets and the page on list comprehensions for how to use those.

8 Sorting Lists

To sort the elements in a list in alphabetical order you have two options:

  1. The sorted() function, which creates a new list (leaving the old one intact, unless you overwrite it):
vowels = ['e', 'a', 'u', 'o', 'i']
# Sort using the sorted() function
vowels_sorted = sorted(vowels)
# Here's the new order
print(vowels_sorted)
## ['a', 'e', 'i', 'o', 'u']
# But the old order is still accessible
print(vowels)
## ['e', 'a', 'u', 'o', 'i']
  1. The .sort() method, which sorts in-place and thus augments the list (meaning the original order cannot be restored):
vowels = ['e', 'a', 'u', 'o', 'i']
# Sort using the .sort() method
vowels.sort()
print(vowels)
## ['a', 'e', 'i', 'o', 'u']

In the second example, the old order is forgotten.

9 Sets

Sets are NOT lists, but they are similar! The main differences are that:

  • A set is immutable which means that it cannot be changed once created
  • A set cannot have duplicates; all of its elements are unique
  • A set is unordered: the order of the elements is not remembered by Python
  • A set is not subscriptable and so cannot be indexed with square brackets: if a was a set then running a[0] would result in an error. How can an object where the elements are unordered have a 0th, 1st, 2nd, etc, element?

Sets are created either by using curly brackets (note that the elements in the output are not necessarily in the same order as in the input, due to sets being unordered):

st = {'A', 'B', 'C'}
print(st)
## {'C', 'A', 'B'}

…or by converting a list into a set using the set() function:

ls = ['A', 'B', 'C', 'A', 'B', 'C']
# Convert list to set
st = set(ls)
print(st)
## {'C', 'A', 'B'}

Notice that in the second example the duplicate values were removed. This is because of the second property of sets, as listed above: they cannot have duplicates. Conversion to a set is thus a useful way to find out what the unique values in a list are:

9.1 Finding the Unique Values in Lists

To remove duplicate values from a list, convert it into a set:

ls = ['First', 'Second', 'Third', 'First', 'Second', 'Third']
# Convert list to set
st = set(ls)
print(st)
## {'First', 'Second', 'Third'}

…then convert it back into a list:

# Convert set to list
ls = list(st)
print(ls)
## ['Second', 'First', 'Third']

Notice that this does not preserve order: we would have expected the result to be ['First', 'Second', 'Third'] but instead it was ['Second', 'First', 'Third']. The easiest way to get the unique values in a list in the same order that they appear is to use a Pandas Series object, see here.

9.2 Finding the Duplicated Values in Two Lists

Considering that a set removes duplicates, it seems weird that they can be used to find duplicates. However, if you have two lists, here’s how you can find the values that exist in both of them using a set and a list comprehension:

ls1 = ['First', 'Second', 'Third']
ls2 = ['Third', 'Fourth', 'Fifth']

# Get duplicates in two lists
combo = ls1 + ls2
duplicates = set([x for x in combo if combo.count(x) == 2])

print(duplicates)
## {'Third'}

9.3 Removing Multiple Values from Lists

Another useful property of sets is that they can be subtracted from each other, which is not true of lists. Here’s an example:

st_1 = {'A', 'B', 'C', 'D', 'E', 'F'}
st_2 = {'A', 'B', 'C'}
# Subtract the sets from each other
st = st_1 - st_2
print(st)
## {'F', 'D', 'E'}

We are left with the elements from set 1 minus the elements from set 2. This can be used to remove multiple values from a list at a time:

ls_1 = ['A', 'B', 'C', 'D', 'E', 'F']
# Elements to remove
to_remove = ['A', 'B', 'C']
# Convert lists to sets, subtract them, convert result into a list
ls = list(set(ls_1) - set(to_remove))
print(ls)
## ['F', 'E', 'D']

Similar to above, this has not preserved the order: we got ['F', 'E', 'D'] when we would have expected ['D', 'E', 'F']. See here for the section of the page about list comprehensions that describes how to achieve this.

9.4 Checking if Two Lists are the Same

If two lists have the same elements in the same order, Python will recognise that they are equivalent:

ls_1 = ['A', 'B', 'C']
ls_2 = ['A', 'B', 'C']
# Are the lists the same?
print(ls_1 == ls_2)
## True

However, if the elements are in a different order, a human might consider the lists to be the same but Python will not:

ls_1 = ['A', 'C', 'B']
ls_2 = ['A', 'B', 'C']
# Are the lists the same?
print(ls_1 == ls_2)
## False

Converting the lists to sets can be used to determine if they contain the same unique values (ie whether or not one list contains a value that the other does not, ignoring duplicate values):

st_1 = set(['A', 'C', 'B'])
st_2 = set(['A', 'B', 'C'])
# Are the sets the same?
print(st_1 == st_2)
## True

9.5 Check if a List is a Subset of Another List

If you only have one value you can check if it exists in a list very easily:

val = 1
ls = [1, 2, 3]
# Is val in ls?
print(val in ls)
## True

However, the same method won’t work if you have multiple values:

sub = [1, 2]
ls = [1, 2, 3]
# Is sub in ls?
print(sub in ls)
## False

We got a result of False because Python was searching for a list inside the list ls instead of searching for two separate values (and it didn’t find that list). We could have used a list comprehension instead:

sub = [1, 2]
ls = [1, 2, 3]
# Are the values in sub in ls as well?
booleans = [x in ls for x in sub]
result = all(booleans)
print(result)
## True

However, if we convert to sets we can use the issubset() method which does the job in one line:

sub = [1, 2]
ls = [1, 2, 3]
# Are the values in sub in ls as well?
result = set(sub).issubset(set(ls))
print(result)
## True

10 Tuples

Tuples are similar to lists and sets but, again, they are different to both:

  • A tuple is immutable (like a set, unlike a list) which means that it cannot be changed once created
  • A tuple can have duplicates (like a list, unlike a set)
  • A tuple is ordered: the order of the elements is remembered by Python (like a list, unlike a set)
  • A tuple is subscriptable (like a list, unlike a set) and so can be indexed with square brackets

A tuple is also created by listing elements separated by commas, but this time those elements need to be surrounded by round brackets:

# Create a tuple by using round brackets
tup = (1, '2', 'three')

print(tup)
## (1, '2', 'three')

Due to a weird quirk of Python, when creating a tuple of length 1 you have to have a comma at the end:

tup = (1,)  # The comma is necessary

print(tup)
## (1,)

Alternatively, the tuple() function can be used to convert a list (identifiable from the square brackets) to a tuple:

# Create a tuple by using the function
tup = tuple([1, '2', 'three'])

print(tup)
## (1, '2', 'three')

In fact, a tuple can be created even without anything surrounding the elements!

# Create a tuple without brackets or a function
tup = 1, '2', 'three'

print(tup)
## (1, '2', 'three')

The above example (not using any brackets) is known as tuple packing and the reverse operation is also possible:

# Extract the elements from a tuple
first, second, third = tup

print(first, second, third)
## 1 2 three

This is known as sequence unpacking and works with any sequence (list, set, tuple, etc) on the right-hand-side.

10.1 Properties of a Tuple

Being immutable means they cannot be changed in-place:

# THIS WILL PRODUCE AN ERROR
tup.append(4)

…but they CAN be overwritten:

tup = tup + (4, 5)

print(tup)
## (1, '2', 'three', 4, 5)

They can contain duplicate elements:

tup = (1, 1, 1)

print(tup)
## (1, 1, 1)

They are ordered: the tuples (1, 2, 3) and (3, 2, 1) contain the same elements but because they are in a different order the tuples are NOT equal:

# Are these tuples equal?
boolean = (1, 2, 3) == (3, 2, 1)

print(boolean)
## False

They are subscriptable and so can be indexed using the position of an element to refer to that element:

# Print the 0th element of the tuple
print(tup[0])
## 1

This is, however, the only way to index a tuple - using the numerical positions of elements. You can’t assign names to the elements unless you use a named tuple:

10.2 Named Tuples

These are tuples that allow you to use either index referencing or dot notation:

from collections import namedtuple

# Create a subclass of tuple that has named fields
name = namedtuple('Name', 'first last')

# Create an instance of a named tuple
person1 = name('John', 'Smith')

Index referencing works as per normal:

print(person1[0])
print(person1[1])
## John
## Smith

However, we can now use dot notation as well:

print(person1.first)
print(person1.last)
## John
## Smith

Named tuples are useful in the specific situation where you have objects with details that have both a name and an order, eg the above example of peoples’ names which have components that can be named (“first” and “last”) and which also have an order.

11 Summary

Data Structure Creation Function Surrounded By Mutable Unique Ordered Subscriptable
List list() [] True False True True
Tuple tuple() () or nothing False False True True
Set set() {} False True False False

All of these structures can contain any type of element (numbers, strings, dictionaries, etc; even other lists, sets or tuples) and/or a mixture of types. They all use commas to separate their elements.

⇦ Back