---
jupytext:
  formats: content_md///md:myst,content///ipynb
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.16.4
kernelspec:
  display_name: Python 3 (ipykernel)
  language: python
  name: python3
---

(python:lists-friends:lists)=
# Lists

Lists are *mutable* objects which hold a *flexible number* of other objects. Lists can be regarded as mutable tuples. Indexing syntax is the same, including slicing. Type name for lists is `list`. Calling `list(...)` creates list from several other types (tuples, for instance).

In principle, Python lists can hold different object types. But it is considered bad practice to use this feature. It’s better to have lists made up of objects of the same type.

## List Methods

List objects come with several methods for modifying them.

The `append` method appends an item to a list.

```{code-cell} ipython3
a = []           # empty list
b = [2, 4, 6]    # list with three integer items
b.append(8)      # now the list has four items
print(len(b))    # length of list
```

To concatenate lists use the `+` operator or the `extend` method.

```{code-cell} ipython3
[1, 2, 3, 4] + [9, 8, 7] + [23, 42]
```

```{code-cell} ipython3
a = [1, 2, 3, 4]
a.extend([9, 8, 7])
print(a)
```

With `sort` we may sort a list.

```{code-cell} ipython3
a = [3, 2, 5, 4, 1]
a.sort()
print(a)
```

To search a list use `index`. This method returns the index of the first occurrence of its argument in the list.

```{code-cell} ipython3
a = [3, 2, 5, 4, 1]
print(a.index(5))
```

To remove a list item either use the `del` keyword (remove by index) or the `remove` method (remove by value):

```{code-cell} ipython3
a = [1, 2, 3, 4]

del a[1]
print(a)

a.remove(3)
print(a)
```

(python:lists-friends:lists:copies)=
## Multiple Names and Copies

Mutability of lists may cause troubles and care has to be taken:

```{code-cell} ipython3
a = [1, 2, 3, 4]
b = a

b[0] = 9

print(a)
print(b)
```

In this code snipped a list is created and the name `a` is tied to it. Then this list object gets `b` as a second name. Important: We have one (!) list object with two names, not two lists! Thus, modifying `b` also modifies `a`.

To copy a list use the `copy` method:

```{code-cell} ipython3
a = [1, 2, 3, 4]
b = a.copy()

b[0] = 9

print(a)
print(b)
```

This creates a so called *shallow copy*. A new list object is created and the items of the new list point to exactly the same objects as the original list. If the original list consists of immutable objects (like integers), then modifying the copy will not alter the original.
But if list items point to mutable objects, then altering the objects of the copy will modify the original list. Copying a list including all objects the list points to, is known as *deep copying*. How to automatically deep-copy a list will be discussed later on.

```{code-cell} ipython3
a = [[1, 2], [3, 4]]
b = a.copy()

# alter b (not its items); works because b is a (shallow) copy of a
b.append([5, 6])
print('a:', a)
print('b:', b)

# alter items of b; also alters items of a because b isn't a deep copy of a
b[0][0] = 9
print('a:', a)
print('b:', b)
```

```{figure} list-copies.svg
---
alt: scheme with sequence of memory locations and corresponding names for list with two names, shallow copies and deep copies
scale: 70%
---
Python lists are lists of memory locations (object IDs). Shallow copying only copies the list of memory locations. Deep copying also copies the data at those memory locations.
```
