Physique-Chimie & NSI

Cours complets et originaux de Physique-Chimie & NSI

3-02. Structures de données en Python

Jusqu’à présent, chaque variable contenait une seule valeur : un entier, un flottant, un booléen ou une chaîne de caractères. Mais comment stocker les coordonnées d’un point, la grille d’un jeu de morpion ou la fiche détaillée d’un personnage de jeu de rôle ? Dans ce chapitre, nous allons découvrir les trois structures de données fondamentales de Python : les p-uplets (tuple), les tableaux (list) et les dictionnaires (dict).

Les p-uplets (tuples) : des données figées

  • Définir et manipuler des p-uplets (tuples).
  • Accéder aux éléments par indexation.
  • Comprendre le principe d’immuabilité d’un tuple.
  • Écrire une fonction renvoyant un p-uplet de valeurs.

Qu’est-ce qu’un p-uplet ?

En mathématiques, un couple $(x, y)$ regroupe 2 éléments, un triplet $(x, y, z)$ en regroupe 3. Plus généralement, un ensemble ordonné de $p$ éléments s’appelle un p-uplet. En informatique et en Python, on utilise le terme anglais tuple.

En Python, on déclare un tuple en séparant ses éléments par des virgules, le plus souvent délimités par des parenthèses ( ) :


        point = (4, 7)               # un couple (2-uplet)
        couleur_rgb = (255, 128, 0)   # un triplet (3-uplet)
        vide = ()                    # un tuple vide
        un_seul = (42,)              # attention à la virgule pour un singleton !
    

💡 Le tuple peut contenir des types différents :

Contrairement aux tableaux traditionnels, un tuple peut mélanger des types variés sans problème. Par exemple : personnage = ("Gandalf", 2019, True) regroupe une chaîne de caractères, un entier et un booléen.

Indexation : accéder aux éléments

Comme pour les chaînes de caractères (vues au chapitre 3-01), les éléments d’un tuple sont ordonnés et repérés par un index entier qui démarre toujours à 0.

Index 0 1 2
Élément "Gandalf" 2019 True
Syntaxe perso[0] perso[1] perso[2]

On utilise la fonction len() pour connaître le nombre d’éléments dans un tuple :


        perso = ("Gandalf", 2019, True)
        print(len(perso))     # Affiche 3
        print(perso[0])       # Affiche "Gandalf"
        print(perso[2])       # Affiche True
    

⛔ Attention à l’erreur IndexError !

Si vous essayez d’accéder à perso[3] alors que le tuple n’a que 3 éléments (index 0, 1 et 2), Python lève une erreur immédiate : IndexError: tuple index out of range.

La propriété fondamentale : l’immuabilité

Un tuple est immuable (immutable) : une fois créé en mémoire, il est strictement impossible de modifier, ajouter ou supprimer un élément.


        point = (10, 25)
        point[0] = 12   # ❌ ERREUR : TypeError: 'tuple' object does not support item assignment
    

Mais pourquoi s’embêter avec une structure qu’on ne peut même pas modifier ? 🤔

L’immuabilité est une sécurité formidable pour le développeur ! Quand vous passez des coordonnées géographiques ou des constantes de configuration à une fonction, vous avez la certitude absolue qu’aucune ligne de code ne pourra corrompre ou altérer ces valeurs par accident.

Déballage et retours multiples de fonctions

A. Le déballage (unpacking)

Python permet d’extraire d’un seul coup les valeurs d’un tuple dans des variables distinctes :


        coordonnees = (48.8566, 2.3522)
        latitude, longitude = coordonnees  # Déballage
        print(f"Lat: {latitude}, Long: {longitude}")
    

B. Renvoyer plusieurs valeurs dans une fonction

Au chapitre 1-01, vous avez vu qu’une fonction termine son exécution avec return. Mais comment renvoyer deux ou trois résultats à la fois ? On renvoie simplement un tuple !


        def division_euclidienne(a, b):
            quotient = a // b
            reste = a % b
            return (quotient, reste)  # Renvoie un 2-uplet

        q, r = division_euclidienne(17, 5)
        print(f"17 = 5 × {q} + {r}")  # Affiche : 17 = 5 × 3 + 2
    

Exercices d’application : Les tuples

1. On définit pixel = (255, 128, 64).

  • Que renvoie pixel[1] ?
  • Que provoque l’instruction pixel[0] = 0 ? Pourquoi ?

2. Écrivez une fonction geometrie_rectangle(largeur, hauteur) qui prend en paramètres les dimensions d’un rectangle et renvoie un tuple contenant son périmètre et son aire.

3. Comment appeler cette fonction pour stocker directement le périmètre dans une variable p et l’aire dans une variable a pour un rectangle de largeur 5 et hauteur 3 ?

Les tableaux (listes) : séquences modifiables

  • Lire et modifier les éléments d’un tableau grâce à leurs index.
  • Itérer sur un tableau (par élément ou par indice).
  • Construire un tableau par répétition et par compréhension.
  • Manipuler des tableaux à deux dimensions (matrices) avec la notation a[i][j].

Tableaux vs Listes en Python

En informatique théorique, un tableau est une suite ordonnée de cases de taille déterminée dont tous les éléments ont le même type (ex : un tableau d’entiers ou un tableau de flottants). En Python, ce concept est implémenté par le type list, délimité par des crochets [ ].


        notes = [14, 18, 9, 12, 16]
        jours = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi"]
    

Lecture et modification en place (mutabilité)

Contrairement aux tuples, les tableaux sont mutables : on peut modifier n’importe quel élément directement grâce à son index !


        notes = [14, 18, 9, 12]
        print(notes[2])    # Affiche 9 (l’élément à l’index 2)

        notes[2] = 11      # On remplace 9 par 11
        print(notes)       # Affiche [14, 18, 11, 12]
    

Les deux manières de parcourir un tableau

C’est un savoir-faire incontournable en NSI. Il existe deux façons de parcourir un tableau avec une boucle for :

1. Parcours direct par élément

Idéal quand on a seulement besoin de lire les valeurs.


                temperatures = [18.5, 21.0, 19.2]

                for temp in temperatures:
                    print(f"Température : {temp}°C")
            

2. Parcours par indice avec range()

Indispensable quand on a besoin de connaître la position ou de modifier le tableau.


                temperatures = [18.5, 21.0, 19.2]

                for i in range(len(temperatures)):
                    print(f"Jour {i} : {temperatures[i]}°C")
                    temperatures[i] += 1.0 # Modifie la valeur !
            

Construire un tableau

A. Par répétition (tableau de taille fixe)

Pour créer un tableau d’une taille donnée contenant la même valeur initiale partout :


        zeros = [0] * 8       # [0, 0, 0, 0, 0, 0, 0, 0] (un octet initialisé à 0 !)
        cases = [False] * 5   # [False, False, False, False, False]
    

B. Construction par compréhension

La compréhension de tableau est une syntaxe très élégante et condensée propre à Python. Elle permet de construire un nouveau tableau en appliquant une formule mathématique ou un filtre en une seule ligne.


        # Méthode classique avec boucle :
        carres = []
        for x in range(6):
            carres.append(x**2)

        # ✅ Méthode moderne PAR COMPRÉHENSION :
        carres = [x**2 for x in range(6)]
        # Résultat : [0, 1, 4, 9, 16, 25]
    

On peut également y ajouter une condition if de filtrage :


        # Ne garder que les nombres pairs :
        nombres = [1, 4, 9, 12, 15, 18, 20]
        pairs = [n for n in nombres if n % 2 == 0]
        # pairs vaut : [4, 12, 18, 20]
    

Tableaux à deux dimensions (matrices et grilles)

Pour représenter une grille de jeu, un plateau d’échecs ou une image bitmap en nuances de gris, on utilise un tableau de tableaux.

Col 0 Col 1 Col 2
Ligne 0 grille[0][0] grille[0][1] grille[0][2]
Ligne 1 grille[1][0] grille[1][1] grille[1][2]
Ligne 2 grille[2][0] grille[2][1] grille[2][2]

        grille = [
            [1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]
        ]

        print(grille[1][2])   # Ligne index 1, Colonne index 2 ⟶ Affiche 6
        grille[0][0] = 99     # On modifie le coin haut gauche
    

⚠️ Le piège de la copie de référence en 2D !

Pour créer une grille de 3×3 remplie de 0, on pourrait avoir envie d’écrire [[0] * 3] * 3. C’est une énorme erreur ! Cela crée 3 fois la même ligne en mémoire. Si vous modifiez une case, toute la colonne change !

La seule bonne manière de construire un tableau à 2 dimensions est d’utiliser la compréhension :


            # ✅ Grille indépendante de 3 lignes et 3 colonnes :
            grille = [[0 for col in range(3)] for ligne in range(3)]
        

Exercices d’application : Tableaux et compréhension

1. Soit le tableau t = [5, 10, 15, 20]. Écrivez une boucle qui multiplie par 2 chaque valeur de t en place.

2. Écrivez en une seule ligne par compréhension :

  • La liste des puissances de 2 de $2^0$ à $2^7$ (les fameux poids du binaire !).
  • La liste des entiers de 0 à 50 qui sont divisibles par 5.

3. On modélise un plateau de morpion (3×3) vide avec des espaces " ".
a. Créez ce plateau par compréhension dans une variable plateau.
b. Placez un "X" au centre du plateau.
c. Placez un "O" dans le coin inférieur droit.

Les dictionnaires : données structurées par clés

  • Construire et modifier un dictionnaire (clés et valeurs).
  • Modéliser un enregistrement (p-uplet nommé).
  • Itérer sur un dictionnaire avec keys(), values() et items().
  • Tester l’appartenance d’une clé avec l’opérateur in.

Associer une étiquette à une valeur

Dans un tableau, on accède aux cases par un numéro (0, 1, 2…). Mais dans la vraie vie, on préfère souvent retrouver une information par son nom (par exemple « taille », « prix », « score »).

Un dictionnaire (type dict en Python) stocke des associations clé : valeur. Il se déclare avec des accolades { } :


        # Dictionnaire modélisant un joueur
        joueur = {
            "pseudo": "PixelKnight",
            "niveau": 12,
            "classe": "Guerrier",
            "pv": 85
        }
    

🖐️ Règles sur les clés et les valeurs :

  • Les clés doivent être uniques et immuables (le plus souvent des chaînes de caractères str ou des entiers int).
  • Les valeurs peuvent être de n’importe quel type (entiers, booléens, tuples, listes, ou même d’autres dictionnaires !).

Lire, modifier et ajouter des éléments

L’accès se fait en spécifiant la clé entre crochets : dico[cle].


        # 1. Lecture
        print(joueur["pseudo"])   # Affiche "PixelKnight"

        # 2. Modification d’une valeur existante
        joueur["pv"] = 100

        # 3. Ajout d'une NOUVELLE clé
        joueur["mana"] = 50       # La clé n’existait pas, Python la crée automatiquement !

        # 4. Suppression
        del joueur["classe"]
    

⛔ Attention à l’erreur KeyError !

Si vous tentez d’accéder à une clé qui n’existe pas (ex : joueur["vitesse"]), Python s’arrête sur une erreur KeyError. Pour éviter cela, on teste la présence de la clé avec le mot-clé in :


            if "vitesse" in joueur:
                print(joueur["vitesse"])
            else:
                print("Ce joueur n’a pas de caractéristique de vitesse.")
        

Les 3 méthodes de parcours d’un dictionnaire

Pour parcourir un dictionnaire avec une boucle for, Python dispose de 3 méthodes fondamentales :

Méthode Ce que l’on parcourt Exemple de syntaxe
.keys() Les clés (comportement par défaut) for cle in d.keys(): (ou for cle in d:)
.values() Uniquement les valeurs for val in d.values():
.items() Les couples (clé, valeur) à la fois for cle, val in d.items():

        inventaire = {"épée": 1, "potions": 5, "pièces": 120}

        # Afficher l'inventaire complet :
        for objet, quantite in inventaire.items():
            print(f"- {objet} : {quantite}")
    

Exercices d’application : Les dictionnaires

1. On considère le dictionnaire suivant : scores = {"Alice": 45, "Bob": 38, "Charlie": 52}.

  • Comment augmenter le score de Bob de 10 points ?
  • Comment ajouter la joueuse "Diane" avec un score de 60 ?

2. Écrivez une fonction total_points(dico_scores) qui prend en paramètre un dictionnaire de scores et renvoie la somme de tous les scores.

3. Défi algorithmique classique (l’histogramme d’un texte) :

Écrivez une fonction compter_caracteres(texte) qui prend une chaîne de caractères et renvoie un dictionnaire associant chaque caractère au nombre de fois où il apparaît dans le texte.

Exemple : compter_caracteres("nsi") renvoie {'n': 1, 's': 1, 'i': 1}.

Synthèse : Quelle structure choisir ?

Structure Syntaxe Mutable ? Mode d’accès Quand l’utiliser ?
p-uplet (tuple) (a, b, c) ❌ Non Par indice entier : t[0] Coordonnées fixes $(x, y)$, retours multiples de fonctions, données qu'on ne veut jamais modifier par accident.
Tableau (list) [a, b, c] ✅ Oui Par indice entier : t[0] Collections ordonnées et homogènes d'éléments, grilles 2D (grille[i][j]), données qui grandissent ou changent.
Dictionnaire (dict) {"k": v} ✅ Oui Par clé nommée : d["k"] Enregistrements (fiches avec champs nommés), annuaires, inventaires, tables de comptage.

Mini-Projet : Le Générateur & Visualiseur de Pixel Art 👾

Objectif : Modéliser, manipuler et afficher une image matricielle en Pixel Art en réinvestissant les tuples (coordonnées et couleurs), les tableaux 2D (la grille de pixels), les dictionnaires (palette de couleurs) et le binaire/hexadécimal vu au chapitre 3-01 ! Durée estimée : 1h30.

Contexte & Principe

Une image numérique matricielle n’est rien d’autre qu’un tableau à deux dimensions de pixels. Pour économiser de la mémoire dans les jeux rétro, on n’enregistrait pas la couleur complète de chaque pixel, mais un indice de couleur renvoyant à une palette (un dictionnaire).

Nous allons concevoir un programme capable de manipuler une image de 8×8 pixels, de lui appliquer des filtres graphiques et de l’exporter.


Étape 1 : La palette de couleurs (Dictionnaire et Hexadécimal)

Chaque couleur est définie par son code hexadécimal #RRGGBB (vu au chapitre 3-01) ou un triplet RVB (R, V, B).

  1. Créez une fonction hex_vers_rvb(code_hex) qui prend une chaîne hexadécimale du type "#FF8000" et renvoie un tuple de 3 entiers (R, V, B) compris entre 0 et 255.
    Indice : int("FF", 16) renvoie 255.
  2. Définissez le dictionnaire PALETTE associant des caractères à des émojis de couleur pour l'affichage en console :
    
                    PALETTE = {
                        0: "⬛",  # Fond noir
                        1: "🟩",  # Vert alien
                        2: "⬜",  # Blanc pour les yeux
                        3: "🟥"   # Rouge pour les dégâts
                    }
                

Étape 2 : Création et affichage de la matrice (Tableaux 2D)

L’alien de Space Invaders est modélisé par une grille 8×8 de numéros de couleurs :


        alien = [
            [0, 0, 1, 0, 0, 1, 0, 0],
            [0, 0, 0, 1, 1, 0, 0, 0],
            [0, 0, 1, 1, 1, 1, 0, 0],
            [0, 1, 2, 1, 1, 2, 1, 0],
            [1, 1, 1, 1, 1, 1, 1, 1],
            [1, 0, 1, 1, 1, 1, 0, 1],
            [1, 0, 1, 0, 0, 1, 0, 1],
            [0, 0, 0, 1, 1, 0, 0, 0]
        ]
    
  1. Écrivez une fonction afficher_image(grille, palette) qui parcourt la grille ligne par ligne et affiche les émojis correspondants pour faire apparaître le dessin dans le terminal.
  2. Écrivez une fonction creer_image_vide(largeur, hauteur, couleur_fond) qui renvoie par compréhension une nouvelle matrice remplie avec la couleur couleur_fond.

Étape 3 : Manipulation et filtres graphiques

  1. Tracer un pixel : Écrivez une fonction colorier(grille, coordonnees, couleur) où coordonnees est un tuple (x, y). Attention : x correspond à la colonne et y à la ligne !
  2. Miroir horizontal : Écrivez une fonction miroir_horizontal(grille) qui renvoie une nouvelle grille où chaque ligne est inversée de gauche à droite.
  3. Négatif / Remplacement de couleur : Écrivez une fonction remplacer_couleur(grille, ancienne_c, nouvelle_c) qui remplace toutes les apparitions d’une couleur par une autre (par exemple transformer tout le vert en rouge quand l'alien est touché !).
Correction complète du mini-projet

            # ==========================================
            # ÉTAPE 1 : COULEURS ET PALETTE
            # ==========================================

            def hex_vers_rvb(code_hex):
                # Retire le '#' initial si présent
                if code_hex[0] == "#":
                    code_hex = code_hex[1:]
                r = int(code_hex[0:2], 16)
                v = int(code_hex[2:4], 16)
                b = int(code_hex[4:6], 16)
                return (r, v, b)

            PALETTE = {
                0: "⬛",  # Fond
                1: "🟩",  # Vert
                2: "⬜",  # Blanc (yeux)
                3: "🟥"   # Rouge
            }

            # ==========================================
            # ÉTAPE 2 : MATRICE ET AFFICHAGE
            # ==========================================

            alien = [
                [0, 0, 1, 0, 0, 1, 0, 0],
                [0, 0, 0, 1, 1, 0, 0, 0],
                [0, 0, 1, 1, 1, 1, 0, 0],
                [0, 1, 2, 1, 1, 2, 1, 0],
                [1, 1, 1, 1, 1, 1, 1, 1],
                [1, 0, 1, 1, 1, 1, 0, 1],
                [1, 0, 1, 0, 0, 1, 0, 1],
                [0, 0, 0, 1, 1, 0, 0, 0]
            ]

            def afficher_image(grille, palette):
                for ligne in grille:
                    ligne_str = "".join([palette[pixel] for pixel in ligne])
                    print(ligne_str)

            def creer_image_vide(largeur, hauteur, couleur_fond=0):
                return [[couleur_fond for _ in range(largeur)] for _ in range(hauteur)]

            # ==========================================
            # ÉTAPE 3 : MANIPULATIONS GRAPHIQUES
            # ==========================================

            def colorier(grille, coordonnees, couleur):
                x, y = coordonnees  # Déballage du tuple (colonne x, ligne y)
                grille[y][x] = couleur

            def miroir_horizontal(grille):
                # Inverse chaque ligne par compréhension
                return [ligne[::-1] for ligne in grille]

            def remplacer_couleur(grille, ancienne_c, nouvelle_c):
                nouvelle_grille = []
                for ligne in grille:
                    nouvelle_ligne = [nouvelle_c if pixel == ancienne_c else pixel for pixel in ligne]
                    nouvelle_grille.append(nouvelle_ligne)
                return nouvelle_grille

            # ==========================================
            # TEST ET DÉMONSTRATION
            # ==========================================

            if __name__ == "__main__":
                print("--- Alien original ---")
                afficher_image(alien, PALETTE)

                print("\n--- Alien touché (vert remplacé par rouge) ---")
                alien_blesse = remplacer_couleur(alien, 1, 3)
                afficher_image(alien_blesse, PALETTE)

                print("\n--- Test conversion hexadécimale ---")
                print(f"#FF8000 en RVB = {hex_vers_rvb('#FF8000')}")