Comment utiliser cette page

Cette page complète les fiches déjà vues sur les piles, les files, les listes et les dictionnaires. Elle explique une autre façon de fabriquer une pile ou une file : avec un tableau de taille fixe et une position, plutôt qu'avec les méthodes toutes prêtes d'une liste Python. Elle explique aussi ce qu'est une interface (ou structure de données abstraite), et elle introduit la programmation orientée objet, avec l'exemple de la classe Point.

Utilisation de la démonstration
Une démonstration avance uniquement quand vous cliquez sur « Suivant » ou « Précédent » : rien ne bouge tout seul. Un bouton « Lecture automatique » est disponible si vous préférez laisser les étapes défiler seules, à une vitesse que vous choisissez.
📄 Le code de cette page, en fichiers téléchargeables
Chaque section a son propre fichier, dans le sous-dossier src à côté de cette page. Après avoir cliqué sur un lien pour le télécharger, on l'exécute dans un terminal, dans le dossier où il a été enregistré, avec python3 nom_du_fichier.py (Python doit être installé sur l'ordinateur). Chaque fichier affiche « OK » pour chaque exercice s'il n'y a pas d'erreur.

1. Vocabulaire à connaître avant de commencer

Chaque mot ci-dessous est utilisé exactement dans ce sens dans toute la page.

MotDéfinition précise
Structure de données abstraite (ou interface)Une structure définie uniquement par l'ensemble de ses valeurs possibles et la liste de ses opérations primitives, sans imposer une seule façon de la stocker en mémoire.
ImplémentationUne façon concrète de réaliser une structure de données abstraite en mémoire. Une même interface (par exemple une file) peut avoir plusieurs implémentations différentes.
PréconditionUne condition qui doit être vraie avant d'exécuter une opération pour qu'elle ait un sens (par exemple : « la file n'est pas vide » avant de retirer un élément).
Assertion (assert)Une instruction Python qui vérifie qu'une précondition est vraie, et qui arrête immédiatement le programme avec un message d'erreur si elle est fausse.
Tableau circulaireUn tableau de taille fixe dans lequel, quand on atteint la dernière case, on reprend au début : la case suivant l'indice LEN - 1 est la case d'indice 0.
Modulo (%)Le reste d'une division entière. En Python, (i + 1) % LEN calcule la case suivant l'indice i dans un tableau circulaire de taille LEN : quand i vaut LEN - 1, le résultat revient à 0.

2. Les files : implémentation avec un tableau circulaire

La fiche Les files montre une file simplifiée, construite avec une liste Python (append et pop(0)). Cette section montre une autre implémentation, plus proche de la mémoire d'un ordinateur : un tableau de taille fixe, une position de tête, et un nombre d'éléments.

2.1 — Le triplet qui représente une file

Principe
Une file de taille fixe LEN_FILE est représentée par trois informations : un tableau de LEN_FILE cases, la position de la tête (l'indice du premier élément), et le nombre d'éléments actuellement dans la file. Le tableau est circulaire : quand on atteint la dernière case, on continue à la case 0.

On représente l'état d'une file d'attente à un bureau de vote, avec LEN_FILE = 10. Au départ, quatre électeurs (e1, e2, e3, e4) sont déjà dans la file : e3 et e4 sont aux cases 0 et 1, e1 et e2 sont aux cases 8 et 9, la tête est en position 8 (c'est e1 qui sera servi en premier).

2.2 — Trace pas à pas

case en tête de file (prochain servi) case qui vient de recevoir un nouvel électeur case vide
Tableau de la file (10 cases, circulaire)
position_tete = -
nombre_element = -
Étape 1 / 1
Cette trace reprend exactement l'exemple du cours : sortie de l'électeur en tête, puis arrivée d'un nouvel électeur.

2.3 — Les opérations primitives

OpérationRôle
creer_file()construit une file vide
est_vide_file(file)renvoie True si nombre_element vaut 0
premier_file(file)renvoie l'élément en tête, sans le retirer
ajouter_file(file, element)ajoute un élément à la queue de la file
retirer_file(file)retire et renvoie la file mise à jour après le départ de l'élément en tête

2.4 — Code Python

LEN_FILE = 10

def creer_file():
    """Construit une file vide, de taille fixe LEN_FILE."""
    return {"tableau": ['_'] * LEN_FILE,
            "position_tete": 0,
            "nombre_element": 0}

def est_vide_file(file):
    """Renvoie True si la file ne contient aucun élément."""
    return file["nombre_element"] == 0

def premier_file(file):
    """Renvoie l'élément en tête de file, sans le retirer."""
    assert not(est_vide_file(file)), 'la file est vide'
    pos = file["position_tete"]
    return file["tableau"][pos]

def retirer_file(file):
    """Retourne la file mise à jour après la sortie de l'élément en tête."""
    assert not(est_vide_file(file)), 'la file est vide'
    pos = file["position_tete"]
    file["tableau"][pos] = '_'
    file["nombre_element"] -= 1
    file["position_tete"] = (file["position_tete"] + 1) % LEN_FILE
    return file

def ajouter_file(file, element):
    """Retourne la file mise à jour après l'ajout d'un élément en queue."""
    assert file["nombre_element"] < LEN_FILE, 'la file est pleine'
    pos = file["position_tete"]
    nb_element = file["nombre_element"]
    fin = (pos + nb_element) % LEN_FILE
    file["tableau"][fin] = element
    file["nombre_element"] += 1
    return file
📄 Télécharger ce code
src/files_circulaire.py — contient ce code, ainsi que les tests des Exercices 1 et 2.
Pourquoi calculer une position avec un modulo ?
La queue de la file n'est pas toujours à la fin du tableau : elle est à la case (position_tete + nombre_element) % LEN_FILE. Le % LEN_FILE fait « revenir » l'indice au début du tableau dès qu'il dépasserait la dernière case, exactement comme les aiguilles d'une horloge reviennent à midi après minuit.

2.5 — Complexité

3. Les piles : implémentation avec un tableau et une position de sommet

De la même façon, une pile peut être implémentée avec un tableau de taille fixe LEN_PILE et un entier indiquant la position du sommet. Par convention, une position de sommet égale à -1 signifie que la pile est vide.

3.1 — Trace pas à pas

On reprend l'exemple d'une pile d'assiettes a1, a2, a3, a4, a5, déjà empilées dans cet ordre (a1 tout en bas, a5 au sommet).

case du sommet case qui vient d'être empilée case vide
Tableau de la pile (10 cases)
position_sommet = -
Étape 1 / 1
Cette trace reprend l'exemple du cours : empilement d'une nouvelle assiette, puis dépilement.

3.2 — Les opérations primitives

OpérationRôle
creer_pile()construit une pile vide (position_sommet = -1)
est_vide_pile(pile)renvoie True si position_sommet == -1
sommet_pile(pile)renvoie l'élément au sommet, sans le retirer
empiler(pile, element)ajoute un élément au sommet de la pile
depiler(pile)retire et renvoie la pile mise à jour après le départ de l'élément du sommet

3.3 — Code Python

LEN_PILE = 10

def creer_pile():
    """Construit une pile vide, de taille fixe LEN_PILE."""
    return {"tableau": ['_'] * LEN_PILE,
            "position_sommet": -1}

def est_vide_pile(pile):
    """Renvoie True si la pile ne contient aucun élément."""
    return pile["position_sommet"] == -1

def sommet_pile(pile):
    """Renvoie l'élément au sommet de la pile, sans le retirer."""
    assert not(est_vide_pile(pile)), 'la pile est vide'
    return pile["tableau"][pile["position_sommet"]]

def empiler(pile, element):
    """Retourne la pile mise à jour après l'empilement d'un élément au sommet."""
    assert pile["position_sommet"] < LEN_PILE - 1, 'la pile est pleine'
    pile["tableau"][pile["position_sommet"] + 1] = element
    pile["position_sommet"] += 1
    return pile

def depiler(pile):
    """Retourne la pile mise à jour après le dépilement de son sommet."""
    assert not(est_vide_pile(pile)), 'la pile est vide'
    pile["tableau"][pile["position_sommet"]] = '_'
    pile["position_sommet"] -= 1
    return pile
📄 Télécharger ce code
src/piles_tableau.py — contient ce code, ainsi que les tests de l'Exercice 3.

3.4 — Complexité

4. La liste vue comme une interface (structure de données abstraite)

4.1 — Définition

Principe
Une interface de liste est définie uniquement par ses opérations : peu importe comment elle est stockée en mémoire, du moment que ces opérations existent et se comportent de la façon attendue.
OpérationRôle
creer_liste()renvoie la liste vide
ajouter_en_tete(x, L)insère l'élément x en tête de la liste L
supprimer_en_tete(L)supprime l'élément de tête de L
est_vide(L)renvoie True si L ne contient aucun élément
tete(L)renvoie l'élément de tête de L
queue(L)renvoie la liste L privée de son élément de tête
Remarque du cours
Les piles et les files sont des cas particuliers de listes.

4.2 — Une implémentation avec des tuples imbriqués

On peut implémenter cette interface avec des tuples Python : la liste vide est représentée par None, et chaque élément ajouté « enveloppe » le reste de la liste dans un couple (valeur, reste_de_la_liste).

def creer_liste():
    return None

def ajouter_en_tete(x, L):
    return (x, L)

def tete(L):
    return L[0]

def queue(L):
    return L[1]

def est_vide(L):
    return L == None

4.3 — Trace pas à pas : construire la liste des voyelles

On construit la liste L2 = <'a' ; 'e' ; 'i' ; 'o' ; 'u' ; 'y' ; ∅> en ajoutant les voyelles en tête, en partant de la dernière voyelle de l'ordre alphabétique.

InstructionValeur de la liste après l'instruction
L2 = creer_liste()None
L2 = ajouter_en_tete('y', L2)('y', None)
L2 = ajouter_en_tete('u', L2)('u', ('y', None))
L2 = ajouter_en_tete('o', L2)('o', ('u', ('y', None)))
L2 = ajouter_en_tete('i', L2)('i', ('o', ('u', ('y', None))))
L2 = ajouter_en_tete('e', L2)('e', ('i', ('o', ('u', ('y', None)))))
L2 = ajouter_en_tete('a', L2)('a', ('e', ('i', ('o', ('u', ('y', None))))))

Pour accéder à la voyelle 'i', il faut retirer deux fois la tête (avec queue), puis lire la nouvelle tête :

InstructionRésultat
queue(L2)('e', ('i', ('o', ('u', ('y', None)))))
queue(queue(L2))('i', ('o', ('u', ('y', None))))
tete(queue(queue(L2)))'i'
Combien d'étapes pour atteindre l'élément de rang i ?
Il faut exactement i appels à queue pour atteindre l'élément de rang i (en partant du rang 0 pour la tête). Contrairement à un tableau, il n'existe pas de raccourci : chaque élément intermédiaire doit être traversé un par un.

4.4 — Fonctions récursives sur cette interface

def nombre_element(L):
    """Nombre d'éléments d'une liste : 0 si vide, sinon 1 + le nombre
    d'éléments de sa queue."""
    if est_vide(L):
        return 0
    else:
        return 1 + nombre_element(queue(L))

def val(i, L):
    """Élément de rang i (0 = tête), en avançant i fois avec queue."""
    assert not(i < 0 or i > nombre_element(L)), 'index hors limite'
    compteur = 0
    temp_liste = L
    while compteur < i:
        temp_liste = queue(temp_liste)
        compteur += 1
    return tete(temp_liste)

def est_dans(x, L):
    """Vrai si x est un élément de la liste L, faux sinon."""
    if est_vide(L):
        return False
    else:
        if x == tete(L):
            return True
        else:
            return est_dans(x, queue(L))
📄 Télécharger ce code
src/listes_interface.py — contient l'implémentation avec des tuples et les fonctions récursives ci-dessus, ainsi que les tests des Exercices 4 et 5.

4.5 — Complexité

Différence avec la fiche « Les listes »
Ici, « liste » désigne une interface implémentée par des tuples imbriqués (une liste chaînée), très différente du type list natif de Python (un tableau contigu) vu dans la fiche Les listes. Avec un vrai list Python, l[i] donne un accès direct en O(1) ; ici, il faut suivre queue pas à pas, ce qui coûte O(i) étapes.

5. La programmation orientée objet : classes et objets

5.1 — Vocabulaire

MotDéfinition précise
ClasseLa description d'un ensemble d'objets ayant la même structure de données et les mêmes méthodes.
Objet (ou instance)Une variable dont le type est une classe. On dit qu'un objet est une instance de sa classe.
AttributUne caractéristique (une donnée) associée à un objet, encapsulée à l'intérieur de celui-ci.
MéthodeUne fonction associée à une classe, qui agit sur les attributs d'un objet de cette classe.
Constructeur (__init__)Une méthode spéciale, obligatoire, appelée automatiquement lors de la création d'un objet, pour initialiser ses attributs.
selfDans une méthode, désigne l'objet lui-même sur lequel la méthode est appelée.
Notation pointéeLa syntaxe objet.methode(arguments) utilisée pour appeler une méthode sur un objet.
__repr__Une méthode spéciale qui définit la chaîne de caractères affichée quand on demande à voir un objet (avec print, ou en tapant son nom dans la console).

5.2 — Exemple : la classe Point

On définit une classe Point, pour manipuler des points dans un repère : deux attributs, abscisse et ordonnée, et une méthode deplace pour effectuer une translation.

5.3 — Trace pas à pas dans une console Python

attribut qui vient de changer
A : Point
abscisse—
ordonnée—
 
Étape 1 / 1

5.4 — Code Python (classe complète, avec attribut couleur)

class Point:
    "Un point du plan muni d'un repère"

    # Méthode constructeur
    def __init__(self, x, y, col="#000000"):
        self.abscisse = x
        self.ordonne = y
        self.couleur = col          # couleur au format RVB, ex. "#FF00AA"

    # Méthode deplace : translate le point de (dx, dy)
    def deplace(self, dx, dy):
        self.abscisse += dx
        self.ordonne += dy

    # Méthode rotation180 : symétrique du point par rapport à l'origine
    def rotation180(self):
        self.abscisse, self.ordonne = -self.abscisse, -self.ordonne

    # Méthode pour représenter le point à l'affichage
    def __repr__(self):
        return f'({self.abscisse!r} ; {self.ordonne!r}) couleur : {self.couleur}'
📄 Télécharger ce code
src/point_poo.py — contient la classe Point ci-dessus, ainsi que les tests des Exercices 6 et 7.
Remarque
__init__ et __repr__ sont des méthodes « double underscore » : des noms réservés que Python appelle automatiquement (__init__ à la création d'un objet, __repr__ à son affichage). On ne les appelle jamais soi-même avec la notation pointée.

5.5 — Récapitulatif vocabulaire

6. Le dictionnaire vu comme une interface

La fiche Les dictionnaires présente la syntaxe Python (parcours, get, items...). Cette section présente l'interface abstraite à l'origine des dictionnaires : ce que le dictionnaire doit faire, indépendamment de la façon dont Python l'implémente.

6.1 — Les opérations abstraites

OpérationRôle
ajoutassocier une nouvelle valeur à une nouvelle clé
modifremplacer la valeur associée à une clé déjà existante, sans changer la clé
supprsupprimer une clé, et donc la valeur qui lui est associée
rechretrouver la valeur associée à une clé

6.2 — Pourquoi la recherche est-elle si rapide dans un dictionnaire ?

Recherche dans une listeRecherche dans un dictionnaire
Méthodeparcourir la liste, comparer chaque valeurappliquer une fonction de hachage à la clé, pour obtenir directement une adresse mémoire
Nombre d'étapes, dans le pire casproportionnel au nombre d'éléments : O(n)indépendant du nombre d'éléments : O(1)
Fonction de hachage
Une fonction de hachage transforme une clé (souvent une chaîne de caractères) en une valeur qui sert d'adresse mémoire, permettant d'accéder directement à la donnée associée, sans avoir à parcourir toutes les autres.

7. Points clés à retenir

Revoir une fiche en détail ? → Piles · Files · Listes · Dictionnaires · Retour à l'accueil