Cours Python · Module 4: Données, API et bases de données

Les formats de données dans la jungle

12 min de lecture
Dans cette leçon9

Bienvenue dans le module 4 ! Ici Darwin, avec un nouveau chapitre de notre aventure.

Jusqu'ici tu as appris Python, des bases jusqu'à la POO avancée. Il est maintenant temps de parler du flux de données - comment stocker, échanger et transporter l'information entre les systèmes, tout comme les animaux communiquent dans la jungle !

Dans la nature, les animaux transmettent l'information de multiples façons - les oiseaux par le chant, les abeilles par la danse, les loups par le hurlement. Dans le monde de la programmation, nous utilisons des formats de données - des manières standardisées d'enregistrer l'information !

Qu'est-ce qu'un format de données ?

Un format de données est une manière standardisée d'organiser et d'enregistrer l'information, qui peut être lue par différents systèmes.

Analogie safari : C'est comme un langage de communication universel - que tu sois biologiste de Pologne, scientifique du Japon ou guide du Kenya, vous comprenez tous un tableau de données sur la population des lions !

Les formats les plus populaires :

  • JSON - JavaScript Object Notation - le plus populaire dans les API web
  • CSV - Comma-Separated Values - simple, tabulaire
  • XML - eXtensible Markup Language - structurel, verbeux
  • YAML - YAML Ain't Markup Language - lisible par un humain, pour les configurations

JSON - JavaScript Object Notation

JSON est le format de données le plus important du monde web - léger, lisible, universel !

La structure JSON

1{
2  "species": "Panthera leo",
3  "common_name": "Lion",
4  "population": 120,
5  "endangered": true,
6  "habitats": ["savanna", "steppe"],
7  "last_observation": {
8    "date": "2024-01-15",
9    "location": "Serengeti",
10    "count": 12
11  }
12}

Types de données en JSON :

  • "text" - chaîne de caractères (toujours entre guillemets)
  • 123, 45.67 - nombre
  • true, false - booléen
  • null - absence de valeur
  • [1, 2, 3] - tableau (liste)
  • {"key": "value"} - objet (dictionnaire)

JSON en Python - le module json

Python dispose d'un module json intégré pour travailler avec ce format :

1import json
2
3# Python → JSON (sérialisation)
4species_data = {
5    "species": "Panthera leo",
6    "common_name": "Lion",
7    "population": 120,
8    "endangered": True,
9    "habitats": ["savanna", "steppe"],
10    "last_observation": {
11        "date": "2024-01-15",
12        "location": "Serengeti",
13        "count": 12
14    }
15}
16
17# Convertir en chaîne JSON
18json_string = json.dumps(species_data, indent=2)
19print(json_string)
20"""
21{
22  "species": "Panthera leo",
23  "common_name": "Lion",
24  "population": 120,
25  "endangered": true,
26  "habitats": [
27    "savanna",
28    "steppe"
29  ],
30  "last_observation": {
31    "date": "2024-01-15",
32    "location": "Serengeti",
33    "count": 12
34  }
35}
36"""
37
38# JSON → Python (désérialisation)
39json_text = '{"species": "Loxodonta africana", "population": 450}'
40python_dict = json.loads(json_text)
41print(python_dict["species"])  # "Loxodonta africana"

Lire et écrire des fichiers JSON

1import json
2
3# Écrire dans un fichier
4species_data = {
5    "species": "Panthera leo",
6    "population": 120,
7    "observations": [
8        {"date": "2024-01-15", "count": 12},
9        {"date": "2024-01-20", "count": 8}
10    ]
11}
12
13with open("species_data.json", "w", encoding="utf-8") as file:
14    json.dump(species_data, file, indent=2, ensure_ascii=False)
15
16# Lire depuis un fichier
17with open("species_data.json", "r", encoding="utf-8") as file:
18    loaded_data = json.load(file)
19    print(loaded_data["species"])  # "Panthera leo"

Différence essentielle :

  • json.dumps() / json.loads() - chaîne (dumps = dump string)
  • json.dump() / json.load() - fichier

JSON et Python - correspondance des types

PythonJSON
dictobject
list, tuplearray
strstring
int, floatnumber
Truetrue
Falsefalse
Nonenull
1import json
2
3# Python → JSON
4python_data = {
5    "name": "Darwin",
6    "age": None,
7    "active": True,
8    "coordinates": (51.5, -0.1)  # Tuple → array
9}
10
11json_str = json.dumps(python_data)
12print(json_str)
13# {"name": "Darwin", "age": null, "active": true, "coordinates": [51.5, -0.1]}

Sérialiser des objets de classes personnalisées

Par défaut, JSON ne gère pas les objets de classes personnalisées - il te faut un encodeur sur mesure :

1import json
2from datetime import datetime
3
4class Species:
5    def __init__(self, name, population, last_seen):
6        self.name = name
7        self.population = population
8        self.last_seen = last_seen  # objet datetime
9
10    def to_dict(self):
11        """Convertit en dictionnaire"""
12        return {
13            "name": self.name,
14            "population": self.population,
15            "last_seen": self.last_seen.isoformat()  # datetime → chaîne
16        }
17
18    @classmethod
19    def from_dict(cls, data):
20        """Restaure depuis un dictionnaire"""
21        return cls(
22            name=data["name"],
23            population=data["population"],
24            last_seen=datetime.fromisoformat(data["last_seen"])
25        )
26
27# Sérialisation
28lion = Species("Lion", 120, datetime.now())
29json_data = json.dumps(lion.to_dict(), indent=2)
30print(json_data)
31
32# Désérialisation
33loaded_dict = json.loads(json_data)
34lion_restored = Species.from_dict(loaded_dict)
35print(lion_restored.name)  # "Lion"

CSV - Comma-Separated Values

CSV est un format tabulaire simple - parfait pour les tableurs et les bases de données !

La structure CSV

1species,common_name,population,habitat
2Panthera leo,Lion,120,savanna
3Loxodonta africana,Elephant,450,savanna
4Python regius,Ball Python,85,jungle
5Gorilla gorilla,Gorilla,230,forest

Caractéristiques :

  • Première ligne = en-têtes de colonnes
  • Lignes suivantes = données
  • Séparateur : la virgule (parfois le point-virgule, la tabulation)

CSV en Python - le module csv

1import csv
2
3# ÉCRIRE du CSV
4species_data = [
5    {"species": "Panthera leo", "common_name": "Lion", "population": 120},
6    {"species": "Loxodonta africana", "common_name": "Elephant", "population": 450},
7    {"species": "Python regius", "common_name": "Ball Python", "population": 85}
8]
9
10with open("species.csv", "w", newline="", encoding="utf-8") as file:
11    fieldnames = ["species", "common_name", "population"]
12    writer = csv.DictWriter(file, fieldnames=fieldnames)
13
14    writer.writeheader()  # Écrire les en-têtes
15    writer.writerows(species_data)  # Écrire toutes les lignes
16
17# LIRE du CSV
18with open("species.csv", "r", encoding="utf-8") as file:
19    reader = csv.DictReader(file)
20
21    for row in reader:
22        print(f"{row['common_name']}: {row['population']} individus")
23# Lion: 120 individus
24# Elephant: 450 individus
25# Ball Python: 85 individus

CSV avec des listes (et non des dictionnaires)

1import csv
2
3# Écrire des listes
4data = [
5    ["species", "population", "endangered"],
6    ["Panthera leo", 120, "True"],
7    ["Loxodonta africana", 450, "True"]
8]
9
10with open("species_simple.csv", "w", newline="", encoding="utf-8") as file:
11    writer = csv.writer(file)
12    writer.writerows(data)
13
14# Lire des listes
15with open("species_simple.csv", "r", encoding="utf-8") as file:
16    reader = csv.reader(file)
17    headers = next(reader)  # Première ligne = en-têtes
18
19    for row in reader:
20        species, population, endangered = row
21        print(f"{species}: {population}")

Différents séparateurs

1import csv
2
3# Séparateur : point-virgule (populaire en Europe)
4with open("data_eu.csv", "w", newline="", encoding="utf-8") as file:
5    writer = csv.writer(file, delimiter=";")
6    writer.writerow(["species", "population"])
7    writer.writerow(["Lion", "120"])
8
9# Séparateur : tabulation
10with open("data_tab.tsv", "w", newline="", encoding="utf-8") as file:
11    writer = csv.writer(file, delimiter="\t")
12    writer.writerow(["species", "population"])
13    writer.writerow(["Lion", "120"])

XML - eXtensible Markup Language

XML est un format verbeux et structurel - utilisé en entreprise, pour les configurations et l'échange de données entre systèmes.

La structure XML

1<?xml version="1.0" encoding="UTF-8"?>
2<species_catalog>
3    <species endangered="true">
4        <scientific_name>Panthera leo</scientific_name>
5        <common_name>Lion</common_name>
6        <population>120</population>
7        <habitats>
8            <habitat>savanna</habitat>
9            <habitat>steppe</habitat>
10        </habitats>
11        <observations>
12            <observation>
13                <date>2024-01-15</date>
14                <location>Serengeti</location>
15                <count>12</count>
16            </observation>
17        </observations>
18    </species>
19</species_catalog>

Caractéristiques :

  • Balises hiérarchiques : <tag>value</tag>
  • Attributs : <tag attribute="value">
  • Auto-fermantes : <tag />
  • Plus verbeux que JSON

XML en Python - le module xml.etree.ElementTree

1import xml.etree.ElementTree as ET
2
3# CRÉER du XML
4root = ET.Element("species_catalog")
5
6# Ajouter une espèce
7species = ET.SubElement(root, "species")
8species.set("endangered", "true")  # Attribut
9
10ET.SubElement(species, "scientific_name").text = "Panthera leo"
11ET.SubElement(species, "common_name").text = "Lion"
12ET.SubElement(species, "population").text = "120"
13
14# Habitats
15habitats = ET.SubElement(species, "habitats")
16ET.SubElement(habitats, "habitat").text = "savanna"
17ET.SubElement(habitats, "habitat").text = "steppe"
18
19# Écrire dans un fichier
20tree = ET.ElementTree(root)
21ET.indent(tree, space="  ")  # Python 3.9+ - mise en forme
22tree.write("species.xml", encoding="utf-8", xml_declaration=True)
23
24# LIRE du XML
25tree = ET.parse("species.xml")
26root = tree.getroot()
27
28for species in root.findall("species"):
29    name = species.find("common_name").text
30    population = species.find("population").text
31    endangered = species.get("endangered")  # Attribut
32
33    print(f"{name}: {population} individus (menacé : {endangered})")
34
35    # Habitats
36    habitats = species.find("habitats")
37    for habitat in habitats.findall("habitat"):
38        print(f"  - {habitat.text}")

YAML - YAML Ain't Markup Language

YAML est un format lisible par un humain - populaire dans les configurations (Docker, Kubernetes, CI/CD) !

La structure YAML

1# Commentaire en YAML
2species:
3  scientific_name: Panthera leo
4  common_name: Lion
5  population: 120
6  endangered: true
7  habitats:
8    - savanna
9    - steppe
10  last_observation:
11    date: 2024-01-15
12    location: Serengeti
13    count: 12
14
15# Liste d'espèces
16all_species:
17  - name: Lion
18    population: 120
19  - name: Elephant
20    population: 450
21  - name: Ball Python
22    population: 85

Caractéristiques :

  • L'indentation détermine la hiérarchie (comme en Python !)
  • - marque un élément de liste
  • key: value - paires clé-valeur
  • Très lisible

YAML en Python - la bibliothèque PyYAML

1import yaml
2
3# Installation : pip install pyyaml
4
5# Python → YAML
6species_data = {
7    "species": "Panthera leo",
8    "common_name": "Lion",
9    "population": 120,
10    "endangered": True,
11    "habitats": ["savanna", "steppe"],
12    "last_observation": {
13        "date": "2024-01-15",
14        "location": "Serengeti",
15        "count": 12
16    }
17}
18
19# Convertir en chaîne YAML
20yaml_string = yaml.dump(species_data, default_flow_style=False, allow_unicode=True)
21print(yaml_string)
22
23# YAML → Python
24yaml_text = """
25species: Loxodonta africana
26population: 450
27endangered: true
28"""
29
30python_dict = yaml.safe_load(yaml_text)
31print(python_dict["species"])  # "Loxodonta africana"
32
33# Lecture / écriture de fichier
34with open("config.yaml", "w", encoding="utf-8") as file:
35    yaml.dump(species_data, file, default_flow_style=False, allow_unicode=True)
36
37with open("config.yaml", "r", encoding="utf-8") as file:
38    loaded_data = yaml.safe_load(file)

Exemple safari - système complet de gestion des données

1import json
2import csv
3import xml.etree.ElementTree as ET
4from datetime import datetime
5from typing import List, Dict, Any
6
7class Species:
8    """Classe Species avec export vers différents formats"""
9
10    def __init__(self, scientific_name: str, common_name: str,
11                 population: int, habitat: str, endangered: bool = False):
12        self.scientific_name = scientific_name
13        self.common_name = common_name
14        self.population = population
15        self.habitat = habitat
16        self.endangered = endangered
17        self.observations: List[Dict[str, Any]] = []
18
19    def add_observation(self, date: str, location: str, count: int):
20        """Ajouter une observation"""
21        self.observations.append({
22            "date": date,
23            "location": location,
24            "count": count
25        })
26
27    # === EXPORT VERS JSON ===
28
29    def to_json_dict(self) -> dict:
30        """Convertit en dictionnaire pour JSON"""
31        return {
32            "scientific_name": self.scientific_name,
33            "common_name": self.common_name,
34            "population": self.population,
35            "habitat": self.habitat,
36            "endangered": self.endangered,
37            "observations": self.observations
38        }
39
40    @classmethod
41    def from_json_dict(cls, data: dict) -> 'Species':
42        """Restaure depuis un dictionnaire JSON"""
43        species = cls(
44            scientific_name=data["scientific_name"],
45            common_name=data["common_name"],
46            population=data["population"],
47            habitat=data["habitat"],
48            endangered=data.get("endangered", False)
49        )
50        species.observations = data.get("observations", [])
51        return species
52
53    # === EXPORT VERS CSV ===
54
55    def to_csv_row(self) -> dict:
56        """Convertit en ligne CSV (simplifiée - sans les observations)"""
57        return {
58            "scientific_name": self.scientific_name,
59            "common_name": self.common_name,
60            "population": self.population,
61            "habitat": self.habitat,
62            "endangered": "Oui" if self.endangered else "Non"
63        }
64
65    # === EXPORT VERS XML ===
66
67    def to_xml_element(self) -> ET.Element:
68        """Convertit en élément XML"""
69        species_elem = ET.Element("species")
70        species_elem.set("endangered", str(self.endangered).lower())
71
72        ET.SubElement(species_elem, "scientific_name").text = self.scientific_name
73        ET.SubElement(species_elem, "common_name").text = self.common_name
74        ET.SubElement(species_elem, "population").text = str(self.population)
75        ET.SubElement(species_elem, "habitat").text = self.habitat
76
77        # Observations
78        if self.observations:
79            obs_elem = ET.SubElement(species_elem, "observations")
80            for obs in self.observations:
81                obs_item = ET.SubElement(obs_elem, "observation")
82                ET.SubElement(obs_item, "date").text = obs["date"]
83                ET.SubElement(obs_item, "location").text = obs["location"]
84                ET.SubElement(obs_item, "count").text = str(obs["count"])
85
86        return species_elem
87
88    def __repr__(self) -> str:
89        return f"Species('{self.common_name}', pop={self.population})"
90
91
92class SpeciesDatabase:
93    """Base de données d'espèces avec export vers différents formats"""
94
95    def __init__(self):
96        self.species: List[Species] = []
97
98    def add_species(self, species: Species):
99        """Ajouter une espèce"""
100        self.species.append(species)
101
102    # === JSON ===
103
104    def export_json(self, filename: str):
105        """Exporter vers JSON"""
106        data = {
107            "export_date": datetime.now().isoformat(),
108            "species_count": len(self.species),
109            "species": [s.to_json_dict() for s in self.species]
110        }
111
112        with open(filename, "w", encoding="utf-8") as file:
113            json.dump(data, file, indent=2, ensure_ascii=False)
114
115        print(f"Exporté {len(self.species)} espèces vers {filename}")
116
117    def import_json(self, filename: str):
118        """Importer depuis JSON"""
119        with open(filename, "r", encoding="utf-8") as file:
120            data = json.load(file)
121
122        self.species.clear()
123        for species_data in data["species"]:
124            self.species.append(Species.from_json_dict(species_data))
125
126        print(f"Importé {len(self.species)} espèces depuis {filename}")
127
128    # === CSV ===
129
130    def export_csv(self, filename: str):
131        """Exporter vers CSV"""
132        if not self.species:
133            print("Aucune espèce à exporter")
134            return
135
136        fieldnames = ["scientific_name", "common_name", "population", "habitat", "endangered"]
137
138        with open(filename, "w", newline="", encoding="utf-8") as file:
139            writer = csv.DictWriter(file, fieldnames=fieldnames)
140            writer.writeheader()
141
142            for species in self.species:
143                writer.writerow(species.to_csv_row())
144
145        print(f"Exporté {len(self.species)} espèces vers {filename}")
146
147    def import_csv(self, filename: str):
148        """Importer depuis CSV"""
149        self.species.clear()
150
151        with open(filename, "r", encoding="utf-8") as file:
152            reader = csv.DictReader(file)
153
154            for row in reader:
155                species = Species(
156                    scientific_name=row["scientific_name"],
157                    common_name=row["common_name"],
158                    population=int(row["population"]),
159                    habitat=row["habitat"],
160                    endangered=(row["endangered"].lower() == "oui")
161                )
162                self.species.append(species)
163
164        print(f"Importé {len(self.species)} espèces depuis {filename}")
165
166    # === XML ===
167
168    def export_xml(self, filename: str):
169        """Exporter vers XML"""
170        root = ET.Element("species_database")
171        root.set("export_date", datetime.now().isoformat())
172        root.set("species_count", str(len(self.species)))
173
174        for species in self.species:
175            root.append(species.to_xml_element())
176
177        tree = ET.ElementTree(root)
178        ET.indent(tree, space="  ")
179        tree.write(filename, encoding="utf-8", xml_declaration=True)
180
181        print(f"Exporté {len(self.species)} espèces vers {filename}")
182
183    def list_species(self):
184        """Afficher toutes les espèces"""
185        print(f"\n=== BASE DE DONNÉES DES ESPÈCES ({len(self.species)}) ===")
186        for i, species in enumerate(self.species, 1):
187            status = "Attention : Menacée" if species.endangered else "En sécurité"
188            print(f"{i}. {species.common_name} ({species.scientific_name})")
189            print(f"   Population : {species.population}, Habitat : {species.habitat}, Statut : {status}")
190
191
192# === DÉMONSTRATION ===
193
194print("=== SYSTÈME DE GESTION DES DONNÉES SAFARI ===\n")
195
196# Créer la base de données
197db = SpeciesDatabase()
198
199# Ajouter des espèces
200lion = Species("Panthera leo", "Lion", 120, "savanna", endangered=True)
201lion.add_observation("2024-01-15", "Serengeti", 12)
202lion.add_observation("2024-01-20", "Masai Mara", 8)
203
204elephant = Species("Loxodonta africana", "African Elephant", 450, "savanna", endangered=True)
205elephant.add_observation("2024-01-16", "Amboseli", 35)
206
207python_snake = Species("Python regius", "Ball Python", 85, "jungle", endangered=False)
208
209db.add_species(lion)
210db.add_species(elephant)
211db.add_species(python_snake)
212
213# Afficher les données
214db.list_species()
215
216# EXPORT vers différents formats
217print("\n=== EXPORT ===")
218db.export_json("safari_data.json")
219db.export_csv("safari_data.csv")
220db.export_xml("safari_data.xml")
221
222# IMPORT depuis JSON
223print("\n=== IMPORT DEPUIS JSON ===")
224db2 = SpeciesDatabase()
225db2.import_json("safari_data.json")
226db2.list_species()
227
228# IMPORT depuis CSV
229print("\n=== IMPORT DEPUIS CSV ===")
230db3 = SpeciesDatabase()
231db3.import_csv("safari_data.csv")
232db3.list_species()
233
234print("\nTous les formats fonctionnent correctement !")

Comparaison des formats

FormatAvantagesInconvénientsUsage
JSONLéger, lisible, universel dans le webPas de commentairesAPI web, configs, échange de données
CSVSimple, compatible Excel, petite tailleTableaux seulement, pas de hiérarchieDonnées tabulaires, export/import de tableurs
XMLStructurel, validation par schémaVerbeux, plus complexeEntreprise, SOAP, systèmes historiques
YAMLTrès lisible, gère les commentairesSensible à l'indentationConfigurations (Docker, K8s, CI/CD)

Résumé

Dans cette leçon, tu as appris :

  • Ce que sont les formats de données et pourquoi on les utilise
  • JSON - json.dumps(), json.loads(), json.dump(), json.load()
  • CSV - csv.DictWriter, csv.DictReader, différents séparateurs
  • XML - xml.etree.ElementTree, création et parsing
  • YAML - yaml.dump(), yaml.safe_load(), la lisibilité
  • La conversion d'objets entre formats
  • Un système pratique d'export/import de données

Point de contrôle

Avant de continuer :

  • Tu sais lire et écrire du JSON
  • Tu comprends la différence entre dumps et dump
  • Tu sais travailler avec du CSV (DictWriter/DictReader)
  • Tu sais quand utiliser quel format
  • Tu sais convertir des objets en dictionnaires

Analogie safari : Les formats de données sont des langages de communication universels - de même que les biologistes de différents pays utilisent les noms latins des espèces, les programmeurs utilisent JSON/CSV/XML pour échanger des données !

Dans la prochaine leçon, Darwin t'enseignera HTTP et requests - comment récupérer des données sur Internet et communiquer avec des API !

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. JSON signifie :

  2. 2. Quelle fonction convertit un dictionnaire Python en chaîne JSON ?

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Tri vertical

    Remets dans l'ordre les étapes de lecture du JSON :

  • Éditeur de code

    Enregistre le dictionnaire animal = {'name': 'Tygrys', 'weight': 200} dans le fichier 'animal.json'

  • Éditeur de code

    Lis le fichier 'animals.csv' et affiche toutes les lignes

  • Tri horizontal

    Remets dans l'ordre : json.dumps(data, indent=2)

  • Clique dans l'ordre

    Clique dans l'ordre : with open('file.json', 'w') as f:

  • Tri vertical

    Remets dans l'ordre les étapes d'écriture du JSON :

Articles utiles