Cours Python · Module 4: Données, API et bases de données
Les formats de données dans la jungle
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- nombretrue,false- booléennull- 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
| Python | JSON |
|---|---|
dict | object |
list, tuple | array |
str | string |
int, float | number |
True | true |
False | false |
None | null |
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,forestCaracté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 individusCSV 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: 85Caractéristiques :
- L'indentation détermine la hiérarchie (comme en Python !)
-marque un élément de listekey: 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
| Format | Avantages | Inconvénients | Usage |
|---|---|---|---|
| JSON | Léger, lisible, universel dans le web | Pas de commentaires | API web, configs, échange de données |
| CSV | Simple, compatible Excel, petite taille | Tableaux seulement, pas de hiérarchie | Données tabulaires, export/import de tableurs |
| XML | Structurel, validation par schéma | Verbeux, plus complexe | Entreprise, SOAP, systèmes historiques |
| YAML | Très lisible, gère les commentaires | Sensible à l'indentation | Configurations (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
dumpsetdump - 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. JSON signifie :
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 :