Python List – Definition and Usage

Python has several collection data types — the types that might hold multiple data items at once. Lists are immutable and ordered Python sequence collections.To be more precise, the collections don’t store data items. They keep the references to them. You can have the references to the objects of different types (integers, floating-point numbers, complex numbers, Booleans, strings, other lists or tuples, sets or dictionaries, custom-defined types, and so on) in one list. You can also put a function or method reference in a list. A list can hold zero or more references to the data items.

Lists can be the items of other sequences or mappings (that is lists, tuples, sets, dictionaries, and so on). They can also be passed as arguments to functions and methods.

Lists are built-in, which means that you don’t need to import any package, class, or function to use them.

This article tends to provide a comprehensive introduction of what you can do with lists and how you can use them. It explains:

  • how to create lists
  • what is and how to get the length of a list
  • how to test if an item is in a list
  • list comprehensions
  • how to use lists with the classes map and filter
  • how to access the items of lists with indices
  • slicing and striding
  • how to apply the del statement with lists
  • the methods of the class list
  • how to add and multiply lists
  • how to iterate through lists

Let’s start with creating lists.

Creating Lists

The most usual way to create a list in Python is just putting its comma-separated items inside the brackets. As already mentioned, the items of a list can be of different types:

>>> my_list = [0, 1, 2.8, True, 'python', (3, 4, 5), {11, 12}, 64]
>>> my_list
[0, 1, 2.8, True, 'python', (3, 4, 5), {11, 12}, 64]
>>> type(my_list)
<class 'list'>

You can also create a list instance by providing some sequence as the first and only argument of the initializer of the class list:

>>> my_tuple = (5, 12, 13)
>>> my_other_list = list(my_tuple)
>>> my_other_list
[5, 12, 13]

This approach is often combined with the use of the built-in class range:

>>> list(range(0, 41, 5))
[0, 5, 10, 15, 20, 25, 30, 35, 40]

In Python, strings are sequences, so this also works:

>>> my_list = list('ABCDEFGH')
>>> my_list
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']

Creating empty lists (lists with no elements) is similar. Either you use an empty pair of brackets, or list without arguments:

>>> my_empty_list = []
>>> my_empty_list
[]
>>> my_empty_list = list()
>>> my_empty_list
[]

These are basic ways to create lists. In the sections below, you’ll see several other approaches.

The Length of Lists

You can get the number of items in a list by calling the built-in function len() with the list as the argument:

>>> x = [2.0, 8.0, True]
>>> len(x)
3
>>> y = [2.0, 8.0, True, (0, 1)]
>>> len(y)
4

The list x has three items: two floating-point numbers (2.0 and 8.0) and one Boolean (True). The list y has four items: the first three the same as in x, and the tuple (0, 1).

The length of an empty list is 0:

>>> z = []
>>> len(z)
0

In the Boolean context, an empty list is evaluated as False, while any non-empty list is treated as True:

```python
>>> bool(x)
True
>>> bool(y)
True
>>> bool(z)
False

It’s very Pythonic to exploit this fact in the if statements. For example, you don’t have to do this:

>>> if len(x) > 0:
...     print('there are some elements in the list x')
... else:
...     print('the list x is empty')
...

there are some elements in the list x

Instead, you can use the fact that len(x) has its Boolean value:

>>> if len(x):
...     print('there are some elements in the list x')
... else:
...     print('the list x is empty')
...

there are some elements in the list x

Both code samples work well, but the later is considered more compact and, as already said, more Pythonic.

Testing Membership

Lists support the membership operator in that enables testing whether a list contains some item or not:

>>> x = [2, 8, 11.2, 8, 14, 2, False, 8, 'python rules']
>>> 8 in x
True
>>> 9 in x
False

In the example above, the list x has the item 8; thus, 8 in x returns True. Contrary, x doesn’t contain 9, so 9 in x is False.

You can use the construct not similarly:

>>> 8 not in x
False
>>> 9 not in x
True

Python sets are unordered sequences of unique items. They are more convenient for membership tests than lists. Sometimes it’s a good idea to create the set that corresponds to a list and perform a membership test against that set:

>>> set(x)
{False, 2, 8, 11.2, 14, 'python rules'}
>>> 8 in set(x)
True

Generally, if you need unique items of a list and you don’t care about their order, consider creating the corresponding set.

List Comprehensions

List comprehensions are an effective and clean way to create lists. For example, this is how you can create a list with the powers of the number 2:

>>> my_list = [2**item for item in range(8)]
>>> my_list
[1, 2, 4, 8, 16, 32, 64, 128]

List comprehensions can contain the additional if clause to filter data. For example, let’s create a new list with the items from my_list larger than 50:

>>> my_other_list = [item for item in my_list if item > 50]
>>> my_other_list
[64, 128]

These are just some simple examples. List comprehensions can contain much more complex code.

Using Lists with map and filter

Instead of using list comprehensions, you can create lists in the functional programming style with the built-in classes map and filter. We’ll work with the same examples as in the previous section.

map needs a function and a sequence of values to apply the function to. It generates the sequence of the returned values from the function. For example:

>>> my_list = list(map(lambda item: 2**item, range(8)))
>>> my_list
[1, 2, 4, 8, 16, 32, 64, 128]

filter also takes a function and a sequence of values used as the arguments. It generates a sequence with the values from the original sequence that make the function return True. For example:

>>> my_other_list = list(filter(lambda item: item > 50, my_list))
>>> my_other_list
[64, 128]

However, list comprehensions are considered a preferable way to create lists in most cases.

Accessing Elements with Indices

Lists are ordered collection, and you can access a particular element with the corresponding integer index.

In Python, the indices are zero-based, which means that the index 0 corresponds to the first (leftmost) item, the index 1 to the second, and so on, while the index equal to len(x) – 1 corresponds to the last (rightmost) item of the list x.

This is how you can access and modify the items of a list:

>>> my_list = [12, 2, [0, 1], 5]
>>> my_list[0]
12
>>> my_list[0] = 1
>>> my_list
[1, 2, [0, 1], 5]
>>> my_list[1]
2
>>> my_list[1] = 24
>>> my_list
[1, 24, [0, 1], 5]

Note that the third item is another list. You can access it similarly:

>>> my_list[2]
[0, 1]

You can also access and modify its items:

>>> my_list[2][0]
0
>>> my_list[2][1]
1
>>> my_list[2][0] = 'i'
>>> my_list[2][1] = 'j'
>>> my_list
[1, 24, ['i', 'j'], 5]

Python allows negative integers to be used as indices. Then, -1 corresponds to the last (rightmost) item of a list, -2 to the second last, and so on:

>>> my_list[-1]
5
>>> my_list[-2]
['i', 'j']

Negative indices can be used similarly as positive ones to access and modify the items of lists.

Slicing and Striding

You can extract the references to certain items from a list, as a new list, using slicing and striding with the following notation: my_list[start:stop:step]. start is the inclusive integer index defining where the extraction starts, end is the exclusive integer index showing where the extraction ends, and step is an integer showing how many places to move in a single step. For example:

>>> my_list = list('ABCDEFGH')
>>> my_list
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
>>> my_list[1:6:2]
['B', 'D', 'F']

The extraction starts at the index 1, that is the item ‘B’. step is 2, thus the next item has the index 3 (= 1 + 2). The corresponding item is ‘D’. The next index is 5 (= 3 + 2), and the extracted item is ‘F’. The extraction stops *before* we reach the value of stop that is the index 6 in this case.

If step is omitted, it’s default value of 1 is used:

>>> my_list[1:6]
['B', 'C', 'D', 'E', 'F']

If step is positive and stop is omitted, its default value of len(my_list) is used, that is extraction is finished when the end of the list is reached:

>>> my_list[1:]
['B', 'C', 'D', 'E', 'F', 'G', 'H']

If step is positive and start is omitted, it’s default value of 0 is used, that is extraction starts at the beginning of the list:

>>> my_list[:6]
['A', 'B', 'C', 'D', 'E', 'F']

Following this logic you can get the shallow copy of the list by omitting all three arguments:

>>> my_list[:]
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']

You can extract items backwards (in reversed order) if start is greater than stop and step is negative:

>>> my_list[4:1:-2]
['E', 'C']

It’s possible to get the reversed shallow copy of the list using this fact:

>>> my_list[::-1]
['H', 'G', 'F', 'E', 'D', 'C', 'B', 'A']

In this case, by default, counting starts from the end of the list and finishes after the list beginning is reached.

If a step is negative, the default value of start is -1 and stop is before the beginning of the list by default.

You can also use negative indices for start or stop:

>>> my_list[:-1]
['A', 'B', 'C', 'D', 'E', 'F', 'G']

Slicing and striding can be used to modify lists:

>>> my_list[1:6:2] = ['b', 'd', 'f']
>>> my_list
['A', 'b', 'C', 'd', 'E', 'f', 'G', 'H']

If stop is 1 (or omitted), you can replace a slice with any number of elements:

>>> my_list[2:6] = ['x', 'y']
>>> my_list
['A', 'b', 'x', 'y', 'G', 'H']

This includes zero elements, that is an empty list:

>>> my_list[2:4] = []
>>> my_list
['A', 'b', 'G', 'H']

In the example above, you have actually removed the items ‘x’ and ‘y’ from the list. You can use similar logic to remove all items, that is to clear the list:

>>> my_list[:] = []
>>> my_list
[]

Slicing is very powerful and in some cases, can replace using the methods of the class list.

Using Lists with the del Statement

You can use the del statement to remove the items from lists.

To delete a single item, simply pass it to the del statement:

>>> x = [1, 2, 4, 8, 16, 32, 64, 128]
>>> del x[1]
>>> x
[1, 4, 8, 16, 32, 64, 128]

You can delete multiple items with one del statement similarly:

>>> del x[0], x[1]
>>> x
[4, 16, 32, 64, 128]

The statement del x[0], x[1] deletes the item at the index 0 that is the item 1 first. Then the item 8 becomes the second item, that is the item with the index 1. It’s deleted next.

It’s possible to delete a slice:

>>> del x[1:4]
>>> x
[4, 128]

As an extension of this behavior, you can clear the list, that is to remove all items with slicing:

>>> del x[:]
>>> x
[]

Have in mind that del x deletes the entire variable x and you’d get NameError if you try to access it after that.

list Methods

The class list has several useful methods. Some of them can be used instead of the operations of slicing, deleting, or concatenating.

.count() returns the number of times the item passed as the argument appears in the list:

>>> x = [2, 8, 1, 12, 121, 8, 98, 423]
>>> x.count(8)
2
>>> x.count(9)
0

.index() returns the index of the leftmost occurrence of the item provided as the first argument. The optional parameters start (inclusive, defaults to 0) and end (exclusive) show the indices where the search starts and ends. If no item is found, it raises ValueError:

>>> x.index(8)
1
>>> x.index(8, 1, 5)
1
>>> x.index(8, 2, 8)
5
>>> x.index(9)
Traceback (most recent call last):
  File "", line 1, in
ValueError: 9 is not in list

.reverse() reverses the list in place:

>>> y = [0, 1, True, False]
>>> y.reverse()
>>> y
[False, True, 1, 0]

.sort() sorts the list in place. The optional parameter key (defaults to None) defines the attribute for sorting. The optional parameter reverse (defaults to False) defines whether the sort is in reversed order or not:

>>> x.sort()
>>> x
[1, 2, 8, 8, 12, 98, 121, 423]
>>> x.sort(reverse=True)
>>> x
[423, 121, 98, 12, 8, 8, 2, 1]

The built-in function sorted() can also be used to sort a list. It accepts a list (or other iterable) and the optional parameters key and reverse. It returns a new list, without modifying the original iterable:

>>> x = [2, 8, 1, 12, 121, 8, 98, 423]
>>> sorted(x)
[1, 2, 8, 8, 12, 98, 121, 423]
>>> sorted(x, reverse=True)
[423, 121, 98, 12, 8, 8, 2, 1]
>>> x
[2, 8, 1, 12, 121, 8, 98, 423]

.append() appends the item passed as the argument to the end of the list:

>>> y = [0, 1, True, False]
>>> y.append(1024)
>>> y
[0, 1, True, False, 1024]

You can use the .append() method with a loop to create a list starting with an empty list:

>>> my_list = []
>>> for item in range(8):
...     my_list.append(2**item)
...
>>> my_list
[1, 2, 4, 8, 16, 32, 64, 128]

List comprehensions are a preferred way to accomplish this, especially in such simple cases. The approach with .append() might be useful in more complex scenarios.

.extend() appends all items from the iterable passed as the argument in order to the end of the list:

>>> y.extend([2, 4, 8, 4])
>>> y
[0, 1, True, False, 1024, 2, 4, 8, 4]

.insert() inserts the item provided as the second argument to the position defined with the first argument:

>>> y.insert(0, -1)
>>> y
[-1, 0, 1, True, False, 1024, 2, 4, 8, 4]
>>> y.insert(2, 'python')
>>> y
[-1, 0, 'python', 1, True, False, 1024, 2, 4, 8, 4]
>>> y.insert(len(y), 'xyz')
>>> y
[-1, 0, 'python', 1, True, False, 1024, 2, 4, 8, 4, 'xyz']

.pop() removes the item with the index provided as the argument and returns it. If no arguments are provided, it removes and returns the rightmost item:

>>> y.pop(0)
-1
>>> y
[0, 'python', 1, True, False, 1024, 2, 4, 8, 4, 'xyz']
>>> y.pop()
'xyz'
>>> y
[0, 'python', 1, True, False, 1024, 2, 4, 8, 4]

.remove() removes the leftmost occurrence of the item passed as the argument. If there’s no such item in the list, it raises ValueError:

>>> y.remove(4)
>>> y
[0, 'python', 1, True, False, 1024, 2, 8, 4]
>>> y.remove(9)
Traceback (most recent call last):
  File "", line 1, in
ValueError: list.remove(x): x not in list

.clear() removes all items from the list:

>>> y.clear()
>>> y
[]

.copy() returns a shallow copy of the list. This means that the original and new list contain the references to the same items. Modifying a mutable element from one list affects the other:

>>> z = [[2, 8], 'python', True]
>>> z_copy = z.copy()
>>> z_copy
[[2, 8], 'python', True]
>>> z_copy[0][0] = 100
>>> z
[[100, 8], 'python', True]

The statement z_copy[0][0] = 100 modifies the first item of z_copy, that is the list [2, 8] to [100, 8]. The same list is referenced by z[0]. The shallow copy means that z[0] and z_copy[0] refer to the same object: the modified list. Similarly, z[1] and z_copy[1] refer to the same string and z[2] and z_copy[2] refer to the same Boolean.

Adding and Multiplying Lists

You can concatenate two lists and get the new list with the + operator like this:

>>> x = [1, 10, 100, 1_000, 10_000]
>>> y = [0, False]
>>> x + y
[1, 10, 100, 1000, 10000, 0, False]

The operands x and y remain unchanged:

>>> x
[1, 10, 100, 1000, 10000]
>>> y
[0, False]

However, the operator += modifies the left-hand-side operand by adding it the item of the right-hand-side operand at the end:

>>> x += y
>>> x
[1, 10, 100, 1000, 10000, 0, False]
>>> y
[0, False]

Multiplying lists with integers is exciting. It uses the operator * and returns a new list with repeated items:

>>> [0, 1] * 4
[0, 1, 0, 1, 0, 1, 0, 1]
>>> z = [[2, 8], 'python', True]
>>> z3 = z * 3
>>> z3
[[2, 8], 'python', True, [2, 8], 'python', True, [2, 8], 'python', True]

Be careful here! What is repeated are the references to the actual objects. That means modifying one object will change the corresponding objects as well:

>>> z3[0][0] = 100
>>> z3
[[100, 8], 'python', True, [100, 8], 'python', True, [100, 8], 'python', True]

z3[0] is the reference to the first item of z3, that is the inner list [2, 8]. The thing is that z3[0], z3[3], and z3[6] all refer to the same object [2, 8]. We’ve modified this object (list) by changing its first item from 2 to 100. The changes are visible at the positions z3[3] and z3[6] as well.

The operator *= modifies the left-hand-side operand:

>>> z *= 2
>>> z
[[100, 8], 'python', True, [100, 8], 'python', True]

Be careful when multiplying the lists with the mutable items (such as other lists, sets, or dictionaries)! Changing one item might reflect on others, and this is usually *not* what you want.

Iterating through Lists

Python lists are iterables. They have the method .iter(). This means that you can call the built-in function iter() with a list as the argument, and get the iterator object that corresponds to that list.

That’s the reason why you can iterate through lists like this:

>>> my_list = [0, 1, 2.8, True, 'python', (3, 4, 5), {11, 12}, 64]
>>> for item in my_list:
...     print(item)
...
0
1
2.8
True
python
(3, 4, 5)
{11, 12}
64

If you want to iterate through a list in the reversed order, you can use the built-in class reversed and get the iterator that yields the values starting from the last one:

>>> for item in reversed(my_list):
...     print(item)
...
64
{11, 12}
(3, 4, 5)
python
True
2.8
1
0

Another nice built-in class is enumerate. You can use it to get the tuples, each of which contains one item and its index:

>>> for i, item in enumerate(my_list):
...     print(i, item)
...
0 0
1 1
2 2.8
3 True
4 python
5 (3, 4, 5)
6 {11, 12}
7 64

Iterators and generators are very important in Python. If you want to understand in-depth how the for loops and many other constructs work, they’re worth further exploration.

Conclusions

This article is an introduction to applying lists in Python. It shows how to create lists and modify their items, how to manipulate slices, how to delete items and slices, how to iterate through lists, and so on. Hopefully, it’ll help you to start using lists effectively.

Keep learning!

Thank you for reading!

This article was provided by our teammate Mirko.