# tuple et set : un enregistrement fixe et une collection sans doublons<no value>

## Qu'allons-nous apprendre ?

Deux autres structures de données, chacune avec son propre « superpouvoir » :

- **tuple** — comme une liste, mais **fixe** : on ne peut pas le modifier après sa création.
- **set** — une collection **sans doublons** et sans ordre, qui permet de vérifier très rapidement si une valeur s'y trouve.

## Pourquoi en avons-nous besoin ?

- Un **tuple** convient aux données qui représentent **un seul enregistrement à structure fixe** : un point `(x, y)`, une date `(2026, 10, 6)`, une ligne d'une base de données `(7, "Dana", 24)`. Dans les thèmes backend et bases de données, **chaque ligne renvoyée par la base de données arrivera sous forme de tuple !** Le fait d'être fixe garantit qu'aucun code ne modifie un enregistrement « par erreur ».
- Un **set** convient aux cas où l'on veut l'**unicité** : combien d'utilisateurs **différents** ont visité le site aujourd'hui ? Quelles étiquettes a un article ? Qui est inscrit à deux cours à la fois ?

## Explication à partir des bases

### tuple

Un **tuple** est une séquence **ordonnée** de valeurs, **qu'on ne peut pas modifier** (Immutable). Une fois créé, on ne peut ni ajouter, ni supprimer, ni remplacer d'éléments.

- S'écrit avec des parenthèses **rondes** : `(1, 2, 3)`.
- L'accès aux éléments se fait comme dans une liste : `point[0]`, `point[-1]`, `len(point)`, `in`.
- Il n'y a ni `append`, ni `remove`, ni affectation à un élément.

**L'Unpacking (décomposition) :** on peut « déballer » un tuple directement dans des variables :

```python
student = ("Dana", 24, "Haifa")
name, age, city = student
```

Nous l'avons déjà utilisé sans nous en rendre compte : `for name, age in attempts:` et `for position, name in enumerate(...)`.

{{< box "analogy" "Analogie" >}}
Une **liste** est comme une liste de courses sur un tableau blanc : on ajoute, on efface, on modifie. Un **tuple** est comme la date de naissance sur une carte d'identité : imprimée une fois, et personne n'est censé la modifier.
{{< /box >}}

### set

Un **set** (ensemble) est une collection de valeurs **uniques** :

- **Pas de doublons** — ajouter une valeur qui existe déjà ne fait rien.
- **Pas d'ordre** — il n'y a pas d'indices. `my_set[0]` est une erreur. L'ordre à l'affichage peut être quelconque.
- **Modifiable** — on peut ajouter et supprimer.
- **Un test `in` très rapide**, même avec un million d'éléments. Dans une liste, `in` parcourt élément par élément.
- S'écrit avec des accolades : `{1, 2, 3}`. **Mais** un set vide se crée avec `set()`, car `{}` est un dict vide (la leçon suivante).

### Les opérations ensemblistes

| Opération | Opérateur | Signification |
|---|---|---|
| Union | `a \| b` | Tout ce qui est dans a **ou** dans b |
| Intersection | `a & b` | Ce qui est **à la fois** dans a **et** dans b |
| Différence | `a - b` | Ce qui est dans a **et pas** dans b |

## Syntaxe

```python
# tuple
point = (3, 5)
single = (7,)              # a tuple with ONE item needs a comma!
x, y = point               # unpacking
point[0]                   # 3

# set
tags = {"python", "web"}
empty_set = set()          # NOT {} - that is an empty dict
tags.add("api")            # add one item
tags.remove("web")         # remove (KeyError if missing)
tags.discard("xyz")        # remove if present, no error if missing
"python" in tags           # fast membership test
unique = set([1, 1, 2])    # {1, 2} - remove duplicates from a list

a | b    # union
a & b    # intersection
a - b    # difference
```

## Premier exemple de code

`week-2/tuples_demo.py` :

```python
student = ("Dana", 24, "Haifa")

print("Full record:", student)
print("Name:", student[0])
print("Fields:", len(student))

name, age, city = student
print(name, "is", age, "and lives in", city)

student[1] = 25
```

**Sortie :**

```text
Full record: ('Dana', 24, 'Haifa')
Name: Dana
Fields: 3
Dana is 24 and lives in Haifa
Traceback (most recent call last):
  File "tuples_demo.py", line 10, in <module>
    student[1] = 25
TypeError: 'tuple' object does not support item assignment
```

## Analyse du code ligne par ligne

| Ligne | Explication |
|---|---|
| `student = ("Dana", 24, "Haifa")` | Un tuple de 3 champs. **La position détermine le sens** : 0 = nom, 1 = âge, 2 = ville. |
| `student[0]` | Accès comme dans une liste. |
| `name, age, city = student` | **Unpacking** : le premier élément va dans `name`, le deuxième dans `age`, le troisième dans `city`. Le nombre de variables doit être égal au nombre d'éléments. |
| `student[1] = 25` | Une tentative de modification, et une `TypeError` est lancée. **Ce n'est pas un défaut, c'est la protection** qu'apporte le tuple. |

Vous voulez « modifier » un tuple ? Créez-en un **nouveau** : `student = (student[0], 25, student[2])`.

## Un autre exemple

**Analyser les utilisateurs d'un site avec un set.** `week-2/visitors.py` :

```python
monday_visits = ["dana", "avi", "dana", "noa", "avi", "dana"]
tuesday_visits = ["noa", "yossi", "rina", "noa"]

monday = set(monday_visits)
tuesday = set(tuesday_visits)

print("Monday page views:", len(monday_visits))
print("Monday unique visitors:", len(monday), monday)

print("Visited on both days:", monday & tuesday)
print("Visited on any day:", monday | tuesday)
print("Only on Monday:", monday - tuesday)

tuesday.add("dana")
tuesday.add("noa")
print("Tuesday after adds:", len(tuesday), "unique")
print("Did yossi visit on Monday?", "yossi" in monday)
```

**Sortie (l'ordre des éléments entre `{}` peut être différent chez vous) :**

```text
Monday page views: 6
Monday unique visitors: 3 {'avi', 'noa', 'dana'}
Visited on both days: {'noa'}
Visited on any day: {'dana', 'yossi', 'rina', 'avi', 'noa'}
Only on Monday: {'avi', 'dana'}
Tuesday after adds: 4 unique
Did yossi visit on Monday? False
```

**Analyse :**

- `set(monday_visits)` — convertit une liste en set, et les doublons disparaissent : 6 vues, 3 visiteurs.
- `monday & tuesday` — **intersection** : seulement ceux qui apparaissent dans les deux (`noa`).
- `monday | tuesday` — **union** : tous ceux qui ont visité, chacun une seule fois.
- `monday - tuesday` — **différence** : a visité lundi et pas mardi.
- `tuesday.add("noa")` — `noa` existe déjà, donc rien ne se passe. Seule `dana` a été ajoutée, et il y en a 4 (`noa`, `yossi`, `rina`, `dana`).

### Quand utiliser lequel ? Un tableau comparatif

| | list | tuple | set |
|---|---|---|---|
| S'écrit | `[1, 2]` | `(1, 2)` | `{1, 2}` |
| Ordre / indice | ✓ | ✓ | ✗ |
| Modifiable | ✓ | ✗ | ✓ |
| Doublons | ✓ | ✓ | ✗ |
| Usage typique | Une collection qui grandit et change | Un enregistrement à structure fixe | Unicité et tests d'appartenance |

## Erreur courante

**Erreur 1 : un tuple d'un seul élément sans virgule.**

```python
colors = ("red")
print(type(colors))
```

```text
<class 'str'>
```

Des parenthèses seules ne sont que des parenthèses arithmétiques, comme `(2 + 3)`. Pour créer un tuple d'un seul élément : `("red",)`. **C'est la virgule qui crée le tuple**, pas les parenthèses.

**Erreur 2 : `{}` comme set vide.**

```python
seen = {}
seen.add("dana")
```

```text
AttributeError: 'dict' object has no attribute 'add'
```

`{}` est un **dict** vide. Un set vide = `set()`.

**Erreur 3 : attendre un ordre ou un indice dans un set.**

```python
names = {"dana", "avi"}
print(names[0])
```

```text
TypeError: 'set' object is not subscriptable
```

Un set n'a pas de « premier ». Si vous avez besoin d'un ordre, convertissez-le en liste triée : `sorted(names)`.

**Erreur 4 : un Unpacking avec un nombre de variables qui ne correspond pas.**

```python
name, age = ("Dana", 24, "Haifa")
```

```text
ValueError: too many values to unpack (expected 2)
```

<!-- 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: L'Unpacking

```python
student = ("Dana", 24, "Haifa")
```

Décomposez le tuple en trois variables en une seule ligne, et affichez une phrase à leur sujet. Essayez ensuite de changer l'âge en 25 — que se passe-t-il ?

**Résultat attendu :**

```text
Dana is 24 and lives in Haifa
```

{{< solution >}}
```python
student = ("Dana", 24, "Haifa")

name, age, city = student
print(name, "is", age, "and lives in", city)
```

```text
Dana is 24 and lives in Haifa
```

`student[1] = 25` lancera `TypeError: 'tuple' object does not support item assignment` — un tuple est **Immutable**. Si vous devez le modifier, créez un nouveau tuple : `student = (name, 25, city)`.
{{< /solution >}}

### Exercice 2: Une fonction qui renvoie deux valeurs

Écrivez une fonction `min_max(numbers)` qui renvoie un tuple avec le plus petit et le plus grand. Décomposez le résultat en deux variables.

```python
lowest, highest = min_max([40, 3, 99, 17])
print(lowest, highest)
```

**Résultat attendu :**

```text
3 99
```

{{< solution >}}
```python
def min_max(numbers):
    return (min(numbers), max(numbers))


lowest, highest = min_max([40, 3, 99, 17])
print(lowest, highest)
```

```text
3 99
```

Une fonction renvoie toujours **une seule** valeur — ici un tuple. L'Unpacking côté appelant le transforme en deux variables.
{{< /solution >}}

### Exercice 3: Des visiteurs uniques

```python
visits = ["dana", "avi", "dana", "noa", "avi", "dana"]
```

Affichez combien de vues il y a eu, combien de visiteurs **différents** il y a eu, et si `"tom"` a visité.

**Résultat attendu :**

```text
6 views
3 visitors
False
```

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

unique = set(visits)
print(len(visits), "views")
print(len(unique), "visitors")
print("tom" in unique)
```

```text
6 views
3 visitors
False
```

Un `set` ne contient pas de doublons, donc `len(set(...))` compte combien de valeurs différentes il y a. Un test `in` sur un set est aussi très rapide.
{{< /solution >}}

### Exercice 4: Les opérations ensemblistes

```python
football = {"dana", "avi", "noa", "tom"}
chess = {"noa", "rina", "tom", "yossi"}
```

1. Qui est dans les deux clubs ?
2. Qui est seulement au football ?
3. Combien d'enfants différents y a-t-il au total ?

Affichez les réponses 1 et 2 triées, pour que l'ordre soit fixe.

**Résultat attendu :**

```text
['noa', 'tom']
['avi', 'dana']
6
```

{{< solution >}}
```python
football = {"dana", "avi", "noa", "tom"}
chess = {"noa", "rina", "tom", "yossi"}

print(sorted(football & chess))
print(sorted(football - chess))
print(len(football | chess))
```

```text
['noa', 'tom']
['avi', 'dana']
6
```

- `&` — intersection (dans les deux). `-` — différence (seulement dans le premier). `|` — union (dans au moins l'un des deux).
- Un set n'a pas d'ordre fixe, donc `sorted` renvoie une liste triée pour l'affichage.
{{< /solution >}}

### Exercice 5: list, tuple ou set ?

Pour chaque cas — quelle structure utiliseriez-vous ? Expliquez.

1. Les coordonnées d'un point sur une carte : latitude et longitude.
2. Une liste de tâches à laquelle vous ajoutez et retirez, dans l'ordre.
3. Les adresses e-mail de tous les inscrits — sans doublons.
4. La file d'attente à la caisse d'un supermarché.

{{< solution >}}
1. **tuple** — un enregistrement fixe de deux valeurs qui ne sont pas censées changer : `(32.08, 34.78)`.
2. **list** — l'ordre compte, et on ajoute et retire.
3. **set** — pas de doublons, et un test rapide pour savoir si une adresse existe déjà.
4. **list** — l'ordre compte, et on prend au début (`pop(0)`).
{{< /solution >}}

<!-- exercises:end -->

## Résumé

- **tuple** `( , )` — ordonné, **non modifiable**. Convient à un enregistrement à structure fixe. Un seul élément : `(x,)`.
- **Unpacking** : `a, b = (1, 2)`. C'est ainsi qu'on renvoie et reçoit plusieurs valeurs d'une fonction.
- **set** `{ }` — pas de doublons, pas d'ordre, pas d'indice. Un set vide = `set()`.
- `set(list)` supprime les doublons. `in` sur un set est très rapide.
- Opérations ensemblistes : `|` union, `&` intersection, `-` différence.
- Dans les thèmes backend et bases de données, les lignes de la base de données reviendront sous forme de **tuple**.

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

## Documentation officielle

- [Python Tutorial — Tuples and Sequences](https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences)
- [Python Tutorial — Sets](https://docs.python.org/3/tutorial/datastructures.html#sets)
- [Built-in Types — Set Types](https://docs.python.org/3/builtins/stdtypes.html#set-types-set-frozenset)

<!-- docs:end -->
