# list : créer, accéder, ajouter, supprimer, rechercher et parcourir<no value>

## Qu'allons-nous apprendre ?

**list** est la structure de données la plus utilisée en Python. Nous l'avons déjà utilisée brièvement. Aujourd'hui, nous allons l'apprendre correctement :

- La créer et accéder aux éléments.
- Ajouter, supprimer et modifier.
- Rechercher.
- Parcourir et trier.

## Pourquoi en avons-nous besoin ?

Presque tout programme contient des **collections** : une liste d'étudiants, les produits d'un panier, les commandes du jour, les lignes d'un fichier, les résultats d'une requête à une base de données (les thèmes backend et bases de données). Quand le nombre n'est pas connu à l'avance, on ne peut pas garder une variable séparée pour chaque élément (`student1`, `student2`...). Une liste en contient **autant que nécessaire**, dans un certain ordre.

## Explication à partir des bases

### Qu'est-ce qu'une liste ?

Une **liste** (list) est une collection **ordonnée** de valeurs :

- **Ordonnée** — chaque élément a une **position** fixe, l'**indice** (index).
- **Modifiable** (mutable) — on peut ajouter, supprimer et modifier des éléments après sa création.
- **Accepte les doublons** — la même valeur peut apparaître plusieurs fois.
- **N'importe quel type** — on peut mélanger (`[1, "a", True]`), mais généralement une liste contient des éléments de même nature.

{{< box "analogy" "Analogie : un train" >}}
Une liste est un train avec des wagons numérotés. Chaque wagon (élément) transporte une charge (une valeur). Le premier wagon est le **numéro 0**, pas le 1. On peut accrocher un wagon à la fin (`append`), placer un wagon au milieu (`insert`), ou détacher un wagon (`remove`/`pop`).
{{< /box >}}

### Les indices

```text
fruits = ["apple", "banana", "cherry", "date"]
index:       0         1         2        3
negative:   -4        -3        -2       -1
```

- Le comptage commence à **0**.
- Un indice **négatif** compte depuis la fin : `-1` = le dernier, `-2` = l'avant-dernier.
- Un indice qui n'existe pas → `IndexError`.

## Syntaxe

```python
# create
empty = []
numbers = [10, 20, 30]

# access and change
numbers[0]          # 10 - first item
numbers[-1]         # 30 - last item
numbers[1] = 25     # change an item
len(numbers)        # 3 - how many items

# add
numbers.append(40)       # add to the end
numbers.insert(0, 5)     # insert at index 0, pushing the rest right

# remove
numbers.remove(25)       # remove the FIRST item equal to 25
last = numbers.pop()     # remove and return the last item
first = numbers.pop(0)   # remove and return the item at index 0
del numbers[0]           # delete by index (returns nothing)
numbers.clear()          # remove everything

# search
30 in numbers            # True / False
numbers.index(30)        # index of the first 30 (ValueError if missing)
numbers.count(30)        # how many times 30 appears

# sort
numbers.sort()               # sorts THIS list in place
numbers.sort(reverse=True)   # descending
sorted(numbers)              # returns a NEW sorted list, original unchanged
```

Une **méthode** est une fonction qui « appartient » à un objet, et qu'on appelle avec un point : `numbers.append(40)`. Nous étudierons les méthodes en profondeur dans le thème POO. Pour l'instant : « une action que la liste sait faire sur elle-même ».

## Premier exemple de code

`week-2/shopping_list.py` :

```python
shopping = ["milk", "bread", "eggs"]
print("Start:", shopping)

shopping.append("cheese")
print("After append:", shopping)

shopping.insert(1, "butter")
print("After insert:", shopping)

shopping.remove("bread")
print("After remove:", shopping)

print("First item:", shopping[0])
print("Last item:", shopping[-1])
print("Items count:", len(shopping))
```

**Sortie :**

```text
Start: ['milk', 'bread', 'eggs']
After append: ['milk', 'bread', 'eggs', 'cheese']
After insert: ['milk', 'butter', 'bread', 'eggs', 'cheese']
After remove: ['milk', 'butter', 'eggs', 'cheese']
First item: milk
Last item: cheese
Items count: 4
```

## Analyse du code ligne par ligne

| Ligne | Ce qui est arrivé à la liste |
|---|---|
| `shopping = [...]` | Créée avec 3 éléments : indices 0, 1, 2. |
| `.append("cheese")` | Ajouté **à la fin**. Maintenant 4 éléments. |
| `.insert(1, "butter")` | Inséré **à l'indice 1**. Ce qui s'y trouvait (`bread`) et ce qui suivait s'est décalé vers la droite. |
| `.remove("bread")` | Supprimé **par valeur** : Python cherche la première occurrence de `"bread"` et la supprime. |
| `shopping[0]` | Le premier élément. |
| `shopping[-1]` | Le dernier élément, sans savoir combien il y en a. |
| `len(shopping)` | Le nombre d'éléments. |

**Remarquez :** `append`, `insert` et `remove` **modifient la liste elle-même** et ne renvoient pas de nouvelle liste. C'est pourquoi on n'écrit pas `shopping = shopping.append(...)` (plus de détails dans la section des erreurs).

## Un autre exemple

**Gérer une file d'attente à la caisse** : parcourir, rechercher, `pop` et `enumerate`. `week-2/queue.py` :

```python
queue = ["dana", "avi", "noa", "avi", "yossi"]

print("People in queue:", len(queue))
print("Is noa waiting?", "noa" in queue)
print("noa's position:", queue.index("noa") + 1)
print("How many times avi:", queue.count("avi"))

served = queue.pop(0)
print("Now serving:", served)

print("--- Remaining queue ---")
for position, name in enumerate(queue, start=1):
    print(position, name)

queue.sort()
print("Alphabetical:", queue)
```

**Sortie :**

```text
People in queue: 5
Is noa waiting? True
noa's position: 3
How many times avi: 2
Now serving: dana
--- Remaining queue ---
1 avi
2 noa
3 avi
4 yossi
Alphabetical: ['avi', 'avi', 'noa', 'yossi']
```

**Analyse :**

- `"noa" in queue` — `in` parcourt la liste et vérifie si la valeur existe. Il renvoie un `bool`.
- `queue.index("noa") + 1` — `index` renvoie l'indice (2). On ajoute 1 car pour les gens, la « troisième place » commence à 1.
- `queue.pop(0)` — **retire et renvoie** le premier. C'est la différence avec `remove` : `pop` vous donne la valeur retirée, pour qu'on puisse l'utiliser (`served`).
- `enumerate(queue, start=1)` — à chaque tour, il donne une **paire** : un numéro courant et l'élément. `for position, name in ...` décompose la paire en deux variables. Sans `start=1`, le comptage commence à 0.
- `queue.sort()` — trie la liste **elle-même**, par ordre alphabétique pour le texte et par ordre croissant pour les nombres.

## Erreur courante

**Erreur 1 : affecter le résultat d'une méthode qui modifie la liste sur place.**

```python
names = ["noa", "avi"]
names = names.append("dana")
print(names)
```

```text
None
```

`append` modifie la liste et **renvoie `None`**. Nous avons affecté le `None` à `names`, et perdu la liste ! La correction : `names.append("dana")` tout seul. C'est la même chose pour `sort()`, `insert()`, `remove()`.

**Erreur 2 : l'Off-by-one sur le dernier indice.**

```python
items = ["a", "b", "c"]
print(items[len(items)])
```

```text
IndexError: list index out of range
```

`len` vaut 3, mais le dernier indice est **2**. Le dernier = `items[-1]` ou `items[len(items) - 1]`.

**Erreur 3 : `remove` sur une valeur qui n'existe pas.**

```python
items.remove("z")
```

```text
ValueError: list.remove(x): x not in list
```

Vérifiez d'abord : `if "z" in items: items.remove("z")`.

**Erreur 4 : supprimer d'une liste pendant qu'on la parcourt.**

```python
numbers = [1, 2, 2, 3]
for n in numbers:
    if n == 2:
        numbers.remove(n)
print(numbers)
```

```text
[1, 2, 3]
```

Nous attendions `[1, 3]`. Quand on supprime un élément, tous les éléments se décalent vers la gauche, et la boucle « saute » l'élément suivant. La solution sûre : **construire une nouvelle liste** avec ce que l'on veut garder :

```python
numbers = [1, 2, 2, 3]
kept = []
for n in numbers:
    if n != 2:
        kept.append(n)
print(kept)   # [1, 3]
```

<!-- exercises:start — generated by scripts/intro6/sync_site_exercises.py; edit scripts/intro6/exercises/ -->
## Exercices

Les exercices n'utilisent que ce qui a été vu dans cette leçon et les précédentes. Essayez d'abord seul, et n'ouvrez la solution qu'ensuite.

### Exercice 1: Les indices

```python
days = ["Sun", "Mon", "Tue", "Wed", "Thu"]
```

Affichez, uniquement avec des indices : le premier jour, le dernier jour (avec un indice négatif), et le jour du milieu.

**Résultat attendu :**

```text
Sun
Thu
Tue
```

{{< solution >}}
```python
days = ["Sun", "Mon", "Tue", "Wed", "Thu"]

print(days[0])
print(days[-1])
print(days[2])
```

```text
Sun
Thu
Tue
```

Les indices commencent à 0. Un indice négatif compte depuis la fin : `-1` est le dernier élément.
{{< /solution >}}

### Exercice 2: Une liste de courses

Partez de `["milk", "bread"]`, et faites ce qui suit dans l'ordre :

1. Ajoutez `"eggs"` à la fin.
2. Ajoutez `"butter"` en première place.
3. Supprimez `"bread"`.
4. Affichez la liste et le nombre d'éléments.

**Résultat attendu :**

```text
['butter', 'milk', 'eggs']
3
```

{{< solution >}}
```python
shopping = ["milk", "bread"]

shopping.append("eggs")
shopping.insert(0, "butter")
shopping.remove("bread")

print(shopping)
print(len(shopping))
```

```text
['butter', 'milk', 'eggs']
3
```

- `append` — à la fin. `insert(0, ...)` — à une position précise.
- `remove` — supprime **par valeur** (la première occurrence).
{{< /solution >}}

### Exercice 3: Une file d'attente

```python
queue = ["dana", "avi", "noa", "avi", "yossi"]
```

1. Vérifiez si `"noa"` est dans la file, et affichez à quelle place elle se trouve (la première place est 1).
2. Affichez combien de fois `"avi"` apparaît.
3. Faites sortir la première personne de la file et affichez `Now serving: <name>`, ainsi que le nombre de personnes qui restent.

**Résultat attendu :**

```text
True
3
2
Now serving: dana
4
```

{{< solution >}}
```python
queue = ["dana", "avi", "noa", "avi", "yossi"]

print("noa" in queue)
print(queue.index("noa") + 1)
print(queue.count("avi"))

served = queue.pop(0)
print("Now serving:", served)
print(len(queue))
```

```text
True
3
2
Now serving: dana
4
```

`pop(0)` retire l'élément et le **renvoie** — c'est pourquoi on peut l'enregistrer dans une variable.
{{< /solution >}}

### Exercice 4: Des statistiques sur des notes

```python
grades = [78, 92, 55, 88, 100, 43, 67]
```

Affichez la note la plus haute, la plus basse, la moyenne (arrondie à un chiffre), et une nouvelle liste des notes qui ont réussi (55 et plus) — sans modifier `grades`.

**Résultat attendu :**

```text
Highest: 100
Lowest: 43
Average: 74.7
Passed: [78, 92, 55, 88, 100, 67]
```

{{< solution >}}
```python
PASSING_GRADE = 55

grades = [78, 92, 55, 88, 100, 43, 67]

print("Highest:", max(grades))
print("Lowest:", min(grades))
print("Average:", round(sum(grades) / len(grades), 1))

passed = []
for grade in grades:
    if grade >= PASSING_GRADE:
        passed.append(grade)
print("Passed:", passed)
```

```text
Highest: 100
Lowest: 43
Average: 74.7
Passed: [78, 92, 55, 88, 100, 67]
```

Le modèle « une nouvelle liste + une boucle + `append` » est la façon de filtrer une liste sans toucher à l'original.
{{< /solution >}}

### Exercice 5: Supprimer pendant une boucle

```python
numbers = [1, 2, 2, 3, 2, 4]
for n in numbers:
    if n == 2:
        numbers.remove(n)
print(numbers)
```

L'intention était de supprimer tous les 2, mais il en est resté un. Écrivez une fonction `remove_all(items, value)` qui renvoie une nouvelle liste sans aucune occurrence de `value`.

**Résultat attendu :**

```text
[1, 3, 4]
```

{{< solution >}}
Quand on supprime d'une liste **pendant** qu'on la parcourt, les éléments se décalent vers la gauche, et la boucle saute l'élément qui est venu prendre la place supprimée. C'est pourquoi on construit plutôt une nouvelle liste :

```python
def remove_all(items, value):
    result = []
    for item in items:
        if item != value:
            result.append(item)
    return result


print(remove_all([1, 2, 2, 3, 2, 4], 2))
```

```text
[1, 3, 4]
```
{{< /solution >}}

<!-- exercises:end -->

## Résumé

- **list** = une collection ordonnée, modifiable, avec doublons. S'écrit avec `[ ]`.
- Un indice commence à **0**. `-1` = le dernier. Un indice qui n'existe pas → `IndexError`.
- Ajouter : `append` (à la fin), `insert` (à une position). Supprimer : `remove` (par valeur), `pop` (par position, et il renvoie l'élément), `del`.
- Rechercher : `in`, `index`, `count`. Parcourir : `for`, et `enumerate` quand on a aussi besoin d'un numéro.
- Les méthodes qui modifient sur place (`append`, `sort`...) renvoient **`None`**. `sorted()` renvoie une **nouvelle** liste.
- Ne supprimez pas d'une liste pendant que vous la parcourez. Construisez une nouvelle liste.

<!-- docs:start — generated by scripts/intro6/site_docs.py; edit the links there -->

## Documentation officielle

- [Python Tutorial — Lists](https://docs.python.org/3/tutorial/introduction.html#lists)
- [Python Tutorial — More on Lists](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists)
- [Built-in Types — Lists](https://docs.python.org/3/builtins/stdtypes.html#lists)

<!-- docs:end -->
