Physique-Chimie & NSI

Cours complets et originaux de Physique-Chimie & NSI

1-01. Les types construits

Les listes et les dictionnaires sont les deux structures de données les plus importantes introduites en classe de Première.

Celles-ci seront utilisées énormément tout au long de la classe de Terminale pour implémenter divers objets abstraits (listes chaînées, piles, files, graphes et arbres).

Ce premier chapitre est un chapitre presque exclusivement de révision.

Les listes

Ce que vous devez déjà savoir

Les listes sont des collections ordonnées et modifiables d’éléments de n’importe quel type, accessibles par leur position (index).

L’indispensable à connaître

Vous devez connaître l’ensemble des commande ci-dessous par cœur !

		
		 # créer une liste
		fruits = ['apple', 'banana', 'mango', 'melon', 'peach']
		
		type(fruits) # → <class 'list'>
		len(fruits) # → nombre d’éléments

		# accéder à un élément
		fruits[0]  # → 'apple (🖐️ premier élément = n°0)
		fruits[2]  # → 'mango'
		fruits[-1] # → 'peach' (dernier élément)
		fruits[-2] # → 'melon' (avant dernier élément)

		# modifier un élément
		fruits[0] = 'orange' # remplace l’élément n°0 par 'orange'
		del fruits[1] # efface l’élément n°1

		# vérifier la présence d'un élément
		'banana' in fruits  # → True
		'kiwi' in fruits    # → False

		# fusionner deux listes
		food = fruits + ["beans", "spinach"] # une nouvelle liste est créée

		# énumérer les éléments
		for f in fruits:
			print(f) # affiche successivement les éléments de la liste

		# énumérer les indices
		for i in range(len(fruits)):
			print(i) # affiche successivement les indices (0, 1, 2…)

		# énumérer les éléments avec index
		for i, fruit in enumerate(fruits):
			print(f"Index {i}: {fruit}")  # affiche l’index et l’élément

		# méthodes très souvent utiles
		fruits.append('lemon') # ajoute 'lemon' à la fin de la liste
		fruit = fruits.pop(1)  # renvoie l’élément 1 et le supprime de la liste
		fruit = fruits.pop()  # renvoie le dernier élément (-1) et le supprime de la liste

		
	

Pour aller plus loin

Il est intéressant de connaître l’existence des commande ci-dessous, mais pas forcément de les mémoriser.


			# découpage (slicing) de listes
			fruits[1:3]    # → ['banana', 'mango'] (éléments de l'index 1 inclus à 3 exclus)
			fruits[:2]     # → ['apple', 'banana'] (du début jusqu'à l'index 2 exclus)
			fruits[2:]     # → ['mango', 'melon', 'peach'] (de l'index 2 inclus jusqu'à la fin)
			fruits[::-1]   # → ['peach', 'melon', 'mango', 'banana', 'apple'] (ordre inversé)

			# autres méthodes
			fruits.count('banana')  # → compte le nombre de fois où 'banana' apparaît
			fruits.extend(['orange', 'grape'])  # ajoute 'orange' et 'grape' à la fin de la liste
			fruits.insert(1, 'orange')  # insère 'orange' en position 1
			fruits.remove('apple')  # supprimme la première occurence de 'apple'
			fruits.clear()  # supprime tous les éléments de la liste
			index = fruits.index('banana')  # → index de la première occurence de 'banana'

			# trier une liste
			fruits.sort()  # tri les éléments (🖐️ la liste est modifiée)
			fruits.sort(key=str.lower) # tri sans tenir compte de la casse
			fruits.sort(key=len) # tri par longueur des chaînes
			fruits.reverse() # inverse l’ordre des éléments (🖐️ la liste est modifiée)
		
			# copie indépendante
			fruits2 = fruits.copy() # fruits2 est une copie indépendante de fruits

			# filtrer une liste par compréhension
			numbers = [1, 2, 3, 4, 5, 6]
			even_numbers = [x for x in numbers if x % 2 == 0]
			# compréhension de liste avec condition else
			numbers = [1, 2, 3, 4, 5]
			result = ["pair" if x % 2 == 0 else "impair" for x in numbers]
			# appliquer une fonction à chaque élément par compréhension
			numbers = [1, 2, 3, 4, 5]
			squares = [x**2 for x in numbers]
			# création de liste avec multiplication
			zeros = [0] * 5     # → [0, 0, 0, 0, 0]
			repeated = [1, 2] * 3  # → [1, 2, 1, 2, 1, 2]
			# conversion d’autres types en liste
			chars = list("hello")  # → ['h', 'e', 'l', 'l', 'o']
			tuple_to_list = list((1, 2, 3))  # → [1, 2, 3]
		

La boucle for sur une liste

Une erreur fréquente consiste à confondre la liste (l'objet itérable) et la variable d'itération (la variable temporaire créée par la boucle).

Dans la syntaxe for element in ma_liste: :

  • ma_liste est l'ensemble des données sur lesquelles on veut faire la boucle.
  • element est une variable créée automatiquement par Python. À chaque tour de boucle, Python y affecte successivement la valeur de chaque élément de la liste, du premier au dernier.

Observons le déroulement pas à pas de ce code :

		
		prenoms = ["Alice", "Bob", "Charlie"]
		for p in prenoms:
			print("Bonjour " + p)
		
	
Tour de boucle Valeur de p Code exécuté Affichage dans la console
Tour n°1 'Alice' print("Bonjour " + 'Alice') Bonjour Alice
Tour n°2 'Bob' print("Bonjour " + 'Bob') Bonjour Bob
Tour n°3 'Charlie' print("Bonjour " + 'Charlie') Bonjour Charlie

Parcours par éléments vs parcours par indices

Il existe trois façons de parcourir une liste en Python selon le besoin. Supposons la liste suivante : [15, 12, 17]

  • Directement par éléments (à privilégier si on n'a pas besoin de la position) :
    
    				for note in notes:
        				print(note) # 'note' contient directement la valeur (ex: 15, 12, ...)
    			
  • Par les indices (si la position dans la liste est nécessaire) :
    
    				for i in range(len(notes)):
        				print(i, notes[i]) # 'i' est l'indice (0, 1, 2...), 'notes[i]' est la valeur
    			
  • À la fois sur les éléments et les indices :
    
    				for i, note in enumerate(notes):
        				print(i, note) # 'i' est l'indice (0, 1, 2...), 'note' est la valeur
    			

Rappel sur la fonction range()

La fonction range(n) renvoit une objet itérable qu’on peut parcourir lors d’une boucle for. La variable d’itération va prendre sucessivement des valeurs entières comprises entre 0 inclus et n exclu. Pratique quand on a besoin que Python compte jusqu’à une valeur donnée.


			for i in range (4):
				print (i) # affiche 0, 1, 2, 3
		

Si on donne deux valeurs à la fonction range, par exemple range(4,7), on va pouvoir compte de 4 inclus à 7 exclus.

Affectation et alias

Une liste est un objet. Une variable ne contient pas directement la liste : elle désigne cet objet.


		a = [1, 2]
		b = a
	

La première instruction crée une liste et associe le nom a à cette liste. La seconde n’en crée pas une nouvelle : elle associe simplement le nom b au même objet. On dit alors que a et b sont des alias.

Ainsi, une modification faite à travers l’un des deux noms est visible à travers l’autre :


		a.append(3)
		print(a)  # [1, 2, 3]
		print(b)  # [1, 2, 3]
	

En revanche, une nouvelle affectation d’un des noms ne modifie pas l’autre :


		a = [1, 2]
		b = a
		a = [4, 5]
		print(a)  # [4, 5]
		print(b)  # [1, 2]
	

Ici, la dernière instruction fait désigner à a une nouvelle liste ; b désigne toujours la première.

Ce phénomène concerne tous les objets, mais il est surtout visible avec les objets mutables, comme les listes et les dictionnaires.

Mais alors, si je veux créer une nouvelle liste indépendante qui est identique à une liste déjà existante, je fais comment ? 😣

La plupart du temps, vous pourrez utiliser la méthode copy(). Cette méthode créer une copie « superficielle » (shallow copy) de votre liste.

		
		fruits = ['apple', 'banana']
		fruits2 = fruits.copy()
		fruits.append('cherry')
		print(fruits2) # ['apple', 'banana']
		
	
Mais pourquoi on appelle ça une copie superficielle ?

Parce que si, dans votre liste, vous avez un élément qui est un objet mutable, cet objet restera le même dans la copie. Compliqué ? 😅 Voici un exemple qui va clarifier ce que je veux dire :

		
		fruits = ['apple', 'banana', [1,2]]
		fruits2 = fruits.copy()
		fruits[2].append(3) # on ajoute 3 à la sous-liste [1,2]
		print(fruits2) # ['apple', 'banana', [1, 2, 3]]
		
	

La liste [1,2] qui est l’élément 2 de la liste fruits reste la même lorsqu’on crée une shallow copy. Donc si vous modifiez cette sous-liste (peu importe comment), ça affectera fruits et fruits2.

Si vous voulez faire une deep copy, il faudra utiliser le module Python copy avec copy.deepcopy().

Les bases

Dans le code ci-dessous, prévoir ce qui va être affiché pour chaque commande print

			
			L = [200, 123, 14, 18, 6, 19]
			print(len(L))
			print(L[0])
			print(L[3])
			print(L[6])

			L[1] = 12
			print(L)
			
		
Correction

				L = [200, 123, 14, 18, 6, 19]
				print(len(L)) # 6
				print(L[0])   # 200
				print(L[3])   # 18
				print(L[6])	  # Erreur car L[6] n’existe pas

				L[1] = 12
				print(L)      # [200, 12, 14, 18, 6, 19]
			

Fonction seuil

Le but de cet exercice est de créer la fonction seuil_liste(L: list, val: int|float) qui prend en paramètre une liste numérique L et une valeur numérique val, et renvoie une liste dans laquelle tous les éléments de L ≥ val ont été remplacés par 0. Après exécution de la fonction, la liste L elle-même ne doit pas être pas modifiée.

Pour cet exercice, nous allons utiliser le mot-clé assert.

			
			assert condition, "message d’erreur optionnel"
			
		

À l’exécution, cette ligne lève une AssertionError si la condition est fausse. Pratique pour débuguer un programme. 😊

1. Compléter l’assertion suivante permettant de vérifier que L est bien de type list.

			
			assert isinstance(L, ...), "L n’est pas une liste"
			
		

2. Compléter le code suivant permettant de vérifier que L est une liste de type numérique, c’est-à-dire que tous ses éléments sont de type numériques (int ou float).


		for elem in L:
			assert isinstance(elem, ...) or isinstance(elem, ...), "élément non numérique"
		

3. Écrire la fonction seuil_liste(L: list, val: int|float). Vous devrez vérifier que L est bien de type list et qu’elle ne contient que des nombres (int ou float), et vous devrez vérifier également que val est bien un nombre.


		def seuil_liste(L: list, val: int|float):
			assert isinstance(L, ...), "L n’est pas une liste"
			# écrire le code manquant

			return liste_finale
		
Correction

				def seuil_liste(L, val):
					assert isinstance(L, list), "L n’est pas une liste"
					new_list = []
					for elem in L:
						assert isinstance(elem, (int, float)), "L n’est pas une liste de nombres"
						if elem >= val: new_list.append(0)
						else: new_list.append(elem)
					return new_list
		

Résultats d’examen

On donne la liste d’élèves suivantes, ainsi que leurs notes à trois épreuves d’examens.

			
			results = [
				["eleve01", [8, 12, 13]],
				["eleve02", ['abs', 10, 7]],
				["eleve03", [15, 18, 19]],
				["eleve04", [14, 'abs', 15]],
				["eleve05", [14, 'abs', 'abs']],
				["eleve06", [10, 10, 11]],
				["eleve07", [12, 8, 8]],
				["eleve08", [13, 15, 11]],
				["eleve09", [14, 15, 16]],
				["eleve10", [12, 13, 12]],
				["eleve11", [9, 11, 12]],
				["eleve12", [16, 16, 14]],
				["eleve13", [10, 'abs', 12]],
				["eleve14", [6, 9, 8]],
				["eleve15", [14, 13, 15]],
				["eleve16", [7, 6, 10]],
				["eleve17", [15, 16, 15]],
				["eleve18", [13, 14, 13]],
				["eleve19", [11, 10, 'abs']],
				["eleve20", [17, 19, 20]],
				["eleve21", [12, 14, 11]],
				["eleve22", [8, 'abs', 9]],
				["eleve23", [15, 12, 13]],
				["eleve24", [9, 7, 10]],
				["eleve25", [16, 13, 'abs']],
				["eleve26", [12, 12, 12]],
				["eleve27", [5, 8, 7]],
				["eleve28", [13, 'abs', 'abs']],
				["eleve29", [18, 17, 16]],
				["eleve30", [11, 12, 14]],
				["eleve31", ['abs', 'abs', 'abs']],
			]
			
		

Écrire la fonction nb_mentions ayant pour paramètres la liste results ainsi qu’un mot-clé pouvant être un des mots suivants exclusivement : recalé, passable, assez bien, bien, très bien. La fonction doit renvoyer le nombre d’élèves ayant la mention correspondant au mot-clé.

  • recalé : moyenne strictement inférieur à 10 ou deux absences.
  • passable : moyenne ≥ 10 et < 12 ou moyen ≥ 10 et une absence (quelle que soit la moyenne).
  • assez bien : moyenne ≥ 12 et < 14.
  • bien : moyenne ≥ 14 et < 16.
  • très bien : moyenne ≥ 16.
Correction

Comme toujours en programmation, il faut découper une tâche « complexe » en plusieurs tâches simple. Commencez par vous posez la question : « quelles sont les différentes opérations simples que je vais devoir intégrer à cette fonction ?

  • Pour un élève donné, calculer sa moyenne, en tenant compte des éventuelles absences. Ceci peut être une première sous-fonction.
  • Pour un élève donné, déterminer le nombre d’absence. Ceci peut être une deuxième sous-fonction.
  • Une fois que les deux sous-fonctions précédentes ont été définies, on peut imaginer une fonction prenant en paramètre un élève et renvoyant sa mention.
  • Enfin, une fois qu’on a les trois sous-fonctions précédentes, on peut écrire notre fonction nb_mentions.

La solution à votre code n’est pas « unique ». Il y a de multiples façons de résoudre ce problème. Gardez seulement en tête qu’un code « de qualité » :

  • Est lisible (le nom des variables et des fonctions est clair et le code est bien structuré, voire commenté).
  • Évite les répétitions inutiles (principe DRY : Don't Repeat Yourself).
  • Est bien découpé en petits éléments indépendants, la tâche de chacun étant simple et bien définie.
  • Reste le plus simple possible pour répondre aux exigences (principe KISS : Keep It Simple, Stupid).

				def get_average(marks):
					"""
					Prend une liste de notes avec éventuellement une ou plusieurs absences
					et renvoie une moyenne (ou None si aucune moyenne ne peut être calculée).
					"""
					numeric_marks = [ mark for mark in marks if isinstance(mark, (int,float)) ]
				
					if len(numeric_marks) == 0:
						return None

					average = sum(numeric_marks) / len(numeric_marks)
					return average

				def get_absences(marks):
					"""Renvoie le nombre d’absences dans une liste de notes"""
					return marks.count("abs")

				def get_mention(student):
					"""Renvoie la mention d’un élève donné"""
					average = get_average(student[1])
					absences = get_absences(student[1])

					if not absences:
						if average >= 16 : return "très bien"
						if average >= 14: return "bien"
						if average >= 12: return "assez bien"
						if average >= 10: return "passable"
					
					if absences == 1 and average >= 10: return "passable"

					return "recalé"

				def nb_mentions(results, mention):
					counter = 0
					for student in results:
						if get_mention(student) == mention:
							counter += 1
					return counter

				test = nb_mentions(results, "bien")
				print (test)
			

Les p-uplets (tuples)

Un tuple (ou p-uplet) est une séquence ordonnée d’éléments, très similaire à une liste, mais avec une différence essentielle : il est immuable (non modifiable après sa création).

Ce qu’il faut retenir


		# Création d’un tuple (avec des parenthèses)
		point = (3, 5)
		couleur = (255, 0, 128)

		# Accès aux éléments (comme une liste)
		print(point[0]) # → 3
		print(len(point)) # → 2

		# 🖐️ IMMUTABILITÉ : impossible de modifier un élément !
		point[0] = 10 # ❌ Lève une erreur : TypeError

		# Dépaquetage (unpacking)
		x, y = point  # x prend la valeur 3, y prend la valeur 5

		# Renvoi de plusieurs valeurs par une fonction
		def coordonnees():
			return 12, 45 # renvoie le tuple (12, 45)
	

Pourquoi utiliser un tuple plutôt qu’une liste ?

  • Sécurité : Garantit que les données ne seront pas modifiées par erreur au cours du programme.
  • Utilisation comme clé de dictionnaire : Comme un tuple est immuable, il peut servir de clé dans un dictionnaire (ex : matrice[(x, y)] = valeur), ce qui est impossible avec une liste.

Les dictionnaires

  • Distinguer la recherche d’une valeur dans une liste et dans un dictionnaire.

Ce que vous devez déjà savoir

Les dictionnaires sont des collections modifiables de paires clé-valeur. On accède aux valeurs par leurs clés, non par position.

Les indispensables

Ces commandes sont à savoir par cœur :


		# Dictionnaire vide
		mon_dict = {}

		# Dictionnaire avec des valeurs initiales
		notes_eleves = {"Alice": 15, "Bob": 12, "Charlie": 18}

		# accès aux valeurs
		notes_eleves["Alice"]		 # accès direct avec la clé → 15
		notes_eleves["David"]		 # lève une KeyError
		notes_eleves.get("David")	 # renvoie None
		notes_eleves.get("David", 0) # Renvoie 0 (valeur par défaut)

		# modification et ajout d’éléments
		notes_eleves["David"] = 14	# ajout
		notes_eleves["Bob"] = 13	# modification

		# suppression d’élément
		del notes_eleves["Charlie"]
		note_bob = notes_eleves.pop("Bob") # suppression et renvoie dans la valeur

		# parcourir un dictionnaire
		for eleve in notes_eleves: # parcourir les clés
			print(f"Élève : {eleve}")

		for eleve in notes_eleves.keys(): # parcourir les clés (explicitement)
			print(f"Élève : {eleve}")

		for note in notes_eleves.values(): # parcourir les valeurs
			print(f"Note : {note}")
		
		for eleve, note in notes_eleves.items(): # parcourir les paires clé-valeur
			print(f"{eleve} a obtenu {note}")

		# vérifier si une clé existe
		if "Alice" in notes_eleves:
			print("Alice est présente dans le dictionnaire")

		# dictionnaires imbriqués
		eleves = {
			"Alice": {"maths": 15, "physique": 17, "français": 14},
			"Bob": {"maths": 12, "physique": 11, "français": 13}
		}
		eleves["Alice"]["physique"] # → 17
	

Pour aller plus loin


			# copie d’un dictionnaire.
			# Attention, comme pour les listes, l’affectation simple crée un alias
			notes_copie = notes_eleves.copy()  # Crée une copie superficielle (shallow copy)

			# fusion de dictionnaires
			notes_groupe1 = {"Eva": 16, "Félix": 14}
			notes_groupe2 = {"Gabriel": 11, "Hélène": 19}
			notes_groupe1.update(notes_groupe2)

			# dictionnaire par compréhension
			carres = {x: x**2 for x in range(1, 6)}
		

L'itération sur un dictionnaire

🖐 Par défaut, quand on fait for x in mon_dictionnaire:, la variable x prend successivement pour valeur chaque CLÉ du dictionnaire, et jamais la valeur associée !

Soit le dictionnaire scores = {"Alice": 10, "Bob": 15} :

  • Itérer sur les clés (par défaut) :
    
    				for nom in scores:
    					print(nom)  # 'nom' prend successivement 'Alice', puis 'Bob'
    					# Pour avoir la valeur, il faut faire : scores[nom]
    					# Si on veut être plus explicite, on peut écrire :
    					# for nom in scores.keys()
    			
  • Itérer uniquement sur les valeurs :
    
    				for points in scores.values():
        				print(points)  # 'points' prend successivement 10, puis 15
    			
  • Itérer simultanément sur la clé ET la valeur (dépaquetage) :
    
    				for nom, points in scores.items():
        				# 'nom' prend la clé, 'points' prend la valeur correspondante !
        				print(f"{nom} a obtenu {points} points")
    			

Recherche dans un dictionnaire

La recherche dans une dictionnaire est un peu plus complexe que dans une liste. Dans une liste, la recherche se fait directement sur les valeurs. Dans un dictionnaire, il faudra faire attention à la notion de clé : valeur. Par défaut, la recherche se fait sur les clés.

		
		# recherche dans une liste
		liste = ["alice", "bob", "charlie"]
		"bob" in liste # → recherche la valeur, renvoie True

		# recherche dans un dictionnaire
		notes_eleves = {"Alice": 15, "Bob": 12, "Charlie": 18}
		eleve = { "prénom": "Alice", "nom": "Alpha", "age": 17 }
		"Alice" in notes_eleves # → True (la clé "Alice" existe)
		"Alice" in eleve # → False (la clé "Alice" n’existe pas)
		"Alice" in eleve.values() # → True (la valeur "Alice" existe)
		
	

Les bases

Charles possède dans sa ferme cinq lapins, sept vaches, deux cochons et quatre chevaux.

1. Écrire un dictionnaire ferme dans lequel les clés sont les types d’animaux (au singulier) possédés par Charles et les valeurs associées les nombres d’animaux correspondants.

2. Quelle instruction permet d’obtenir le nombre de chevaux que possède Charles ?

3. Charles achète douze poules. Quelle instruction permet de mettre à jour le dictionnaire ferme ?

4. Charles achète six nouveaux lapins. Quelle instruction permet de mettre à jour le dictionnaire ferme ?

5. Ecrire une instruction renvoyant la liste des types d’animaux de la ferme de Charles sous la forme d’une liste.

6. Compléter la fonction suivante grâce à sa documentation.


			def nombre_animaux(ferme):
				'''
				ferme est un dictionnaire dont les clés sont les types d’animaux de la ferme
				et les valeurs associées sont les nombres d’animaux correspondants
				Renvoie le nombre total d’animaux de la ferme
				'''
				nb = ...
				for valeur in ...:
					...
				return nb
		

Les instructions suivantes permettent de tester votre code. Elles ne doivent pas lever d’AssertError lors de leur exécution.

			
				assert nombre_animaux({}) == 0
				assert nombre_animaux({'lapin':5, 'vache':7, 'cochon':2, 'cheval':4}) == 18
				assert nombre_animaux({'lapin':2, 'poule':10, 'âne':4, 'mouton':25}) == 41
				assert nombre_animaux(ferme) == 36
			
		

7. Donner l’instruction permettant d’obtenir le nombre de n’importe quel type d’animaux. Si le dictionnaire ferme ne contient pas cette clé, on doit obtenir 0 (pas d’erreur).

Correction

				ferme = {"lapin": 5, "vache": 7, "cochon": 2, "cheval": 4,}

				print(ferme["cheval"])	# question 2
				ferme["poule"] = 12		# question 3
				ferme["lapin"] = ferme["lapin"] + 6	# question 4
				ferme["lapin"] += 6					# question 4 (autre possiilité)

				# question 5
				liste_animaux_1 = list(ferme) # on convertir le dictionnaire en liste
				print(liste_animaux_1)

				# autre possibilité pour la question 5
				liste_animaux_2 = []
				for animal in ferme:
					liste_animaux_2.append(animal)
					
				print(liste_animaux_2)

				def nombre_animaux(ferme):
					'''
					ferme est un dictionnaire dont les clés sont les types d’animaux de la ferme
					et les valeurs associées sont les nombres d’animaux correspondants
					Renvoie le nombre total d’animaux de la ferme
					'''
					nb = 0
					for valeur in ferme.values():
						nb += valeur
					return nb

				# question 6
				test_1 = ferme["mouton"] # provoque une erreur
				test_2 = ferme.get("mouton", 0) # get renvoie la valeur 0 car "mouton" n’est pas une clé de ferme
				print(test_2)
			

Recettes de cuisine

Voici un exemple de données permettant de manipuler un livre de recettes de cuisine à partir de la liste des ingrédients des recettes :

Recette Ingrédients
Gâteau au chocolat chocolat, oeuf, farine, sucre, beurre
Gâteau au yaourt yaourt, oeuf, farine, sucre
Crêpes oeuf, farine, lait, bière
Quatre-quarts oeuf, farine, beurre, sucre
Kouign-amann farine, beurre, sucre

On va modéliser en Python un livre de recettes par un dictionnaire de type dict[str, list[str]] dans lequel :

  • les noms des recettes, de type str, sont les clés ;
  • l’ensemble des ingrédients, de type list[str], sont les valeurs associées.

1. Construire un dictionnaire LivreRecettes correspondant au tableau ci-dessus.

2. Définir une fonction nb_ingredients qui, à partir d’un dictionnaire de recettes et d’un nom de recette, renvoie le nombre d’ingrédients de cette recette. Par exemple, l’instruction nb_ingredients(LivreRecettes, 'Crêpes') doit renvoyer 4.

3. Définir une fonction recipe_with renvoyant la listes des recettes qui utilisent un ingrédient donné. Par exemple, l’instruction recipe_with(LivreRecettes, 'chocolat') renvoie la liste ['Gâteau au chocolat'].

4. Définir une fonction get_all_ingredients qui renvoie la liste de tous les ingrédients des recettes du livre de recettes passé en paramètre. La liste ne doit pas comporter de doublons.

5. Tout livre de recettes contient une table des ingrédients permettant d’associer à chaque ingrédient l’ensemble des recettes qui l’utilisent. Une telle table sera représentée en Python par le type dict[str, list[str]] dans lequel une clé est un ingrédient dont la valeur associée est la liste des recettes qui l’utilisent. Définir une fonction get_ingredient_table qui, étant donné un livre de recettes D, renvoie la table des ingrédients associée.

6. Écrire la fonction get_main_ingredient qui renvoie l’ingrédient le plus utilisé dans un livre de recettes passé en paramètre. On supposera que le livre de recettes contient au moins une recette. De plus, s’il y a plusieurs ingrédients possibles, on renverra le premier trouvé. Par exemple, get_main_ingredient(LivreRecettes) doit renvoyer 'farine'.

7. Certaines personnes sont allergiques à certains ingrédients. On aimerait donc pouvoir ne conserver d’un livre de recettes que celles qui n’utilisent pas un ingrédient donné. Définir une fonction get_recipe_without qui, étant donné un livre de recettes et un ingrédient passés en paramètre, renvoie un nouveau livre de recettes ne contenant que des recettes n’utilisant pas l’ingrédient donné.

8. Écrire une fonction get_recipe_without_multiple qui, étant donnés un livre de recettes et une liste d’ingrédients passés en paramètres, renvoie un nouveau livre de recettes ne contenant que les recettes n’utilisant aucun des ingrédients donnés.

Correction

				# question 1. Le dictionnaire modélisant le livre de recette
				book = {
					"Gâteau au chocolat": ["chocolat", "oeuf", "farine", "sucre", "beurre"],
					"Gâteau au yaourt": ["yaourt", "oeuf", "farine", "sucre"],
					"Crêpes": ["oeuf", "farine", "lait", "bière"],
					"Quatre-quarts": ["oeuf", "farine", "beurre", "sucre"],
					"Kouign-amann": ["farine", "beurre", "sucre"]
				}

				# question 2. nombre d’ingrédients d’une recette donnée
				def nb_ingredients(book, recipe):
					ingredient_list = book.get(recipe, [])
					return len(ingredient_list)

				# question 3. recette utilisant un ingrédient donné
				def recipe_with(book, ingredient):
					recipe_list = []
					for recipe, ingredients in book.items():
						if ingredient in ingredients:
							recipe_list.append(recipe)
					return recipe_list

				# question 4. liste de tous les ingrédients apparaîssant dans le livre
				def get_all_ingredients(book):
					ingredient_list = []
					for ingredients in book.values():
						for ingredient in ingredients:
							if ingredient not in ingredient_list:
								ingredient_list.append(ingredient)
					return ingredient_list

				# question 5. table des ingrédients
				def get_ingredient_table(book):
					ingredients = get_all_ingredients(book)
					ingredient_table = {}
					for ingredient in ingredients:
						recipe_list = []
						for recipe, recipe_ingredients in book.items():
							if ingredient in recipe_ingredients:
								recipe_list.append(recipe)
						ingredient_table[ingredient] = recipe_list
					return ingredient_table

				# question 6. ingrédient principal
				def get_main_ingredient(book):
					ingredients = get_all_ingredients(book)
					all_ingredients_occurence = []
					max_occurence = 0		 # initialisation
					main_ingredient = ""	 # initialisation
					for recipe_ingredients in book.values():
						all_ingredients_occurence.extend(recipe_ingredients)
						# all_ingredients_occurence contient la liste fusionnée
						# de toutes les listes d’ingrédients des recettes
					for ingredient in ingredients:
						ingredient_occurence = all_ingredients_occurence.count(ingredient) # nombre de recettes avec cet ingrédient
						if ingredient_occurence > max_occurence:
							max_occurence = ingredient_occurence
							main_ingredient = ingredient
					return main_ingredient

				# question 7. recette sans un ingrédient donné
				def get_recipe_without(book, ingredient):
					filtered_book = {}
					for recipe, ingredients in book.items():
						if ingredient not in ingredients:
							filtered_book[recipe] = ingredients
					return filtered_book

				# question 8. recette sans plusieurs ingrédients
				def get_recipe_without_multiple(book, banned_ingredients):
					filtered_book = {}
					for recipe, ingredients in book.items():
						target_ingredients = list(set(banned_ingredients) & set(ingredients))
						# target_ingredients contient une liste des ingrédients se trouvant à la fois
						# dans banned_ingredients et dans ingredients
						# voir le paragraphe "pour aller plus loin à propos des objets de type set"
						if len(target_ingredients) == 0:
							filtered_book[recipe] = ingredients
					return filtered_book
			

Les ensembles (set)

En Python, un ensemble (set) est une collection non ordonnée d’éléments uniques. Contrairement aux listes, les ensembles ne peuvent pas contenir de doublons et n’ont pas d’ordre défini. Ils sont particulièrement utiles lorsque l’unicité des éléments est importante et que l’ordre n’a pas d’importance.

Création et utilisation des ensembles


		# Création d’un ensemble
		nombres = {1, 2, 3, 4, 5}
		# ou avec le constructeur set()
		couleurs = set(["rouge", "vert", "bleu"])

		# Tentative d’ajout d’un doublon
		nombres.add(3) # L’ensemble ne change pas car 3 existe déjà
		print(nombres) # {1, 2, 3, 4, 5}

		# Ajout d’un nouvel élément
		nombres.add(6)
		print(nombres) # {1, 2, 3, 4, 5, 6}

		# Suppression d’un élément
		nombres.remove(2)
		print(nombres) # {1, 3, 4, 5, 6}
	

Quand utiliser un ensemble plutôt qu’une liste ?

1. Pour éliminer les doublons : si vous avez une liste avec des éléments répétés et souhaitez les éliminer.


		liste_avec_doublons = [1, 2, 2, 3, 4, 4, 5]
		liste_sans_doublons = list(set(liste_avec_doublons))
		print(liste_sans_doublons) # [1, 2, 3, 4, 5]
	
2. Pour les opérations ensemblistes : les ensembles permettent d’effectuer des opérations mathématiques comme l’union, l’intersection et la différence.

	ensemble_A = {1, 2, 3, 4}
	ensemble_B = {3, 4, 5, 6}

	# Union (tous les éléments des deux ensembles)
	print(ensemble_A | ensemble_B) # {1, 2, 3, 4, 5, 6}

	# Intersection (éléments présents dans les deux ensembles)
	print(ensemble_A & ensemble_B) # {3, 4}

	# Différence (éléments présents dans A mais pas dans B)
	print(ensemble_A - ensemble_B) # {1, 2}
	

3. Pour vérifier l’appartenance : la recherche d’un élément dans un ensemble est plus rapide que dans une liste.


		# Vérification d’appartenance dans une liste vs un ensemble
		grande_liste = list(range(1000000))
		grand_ensemble = set(range(1000000))

		# La recherche dans un ensemble est beaucoup plus rapide
		# surtout pour de grandes collections
		500000 in grand_ensemble # Très rapide
		500000 in grande_liste # Plus lent
	

Utilisez un ensemble quand vous avez besoin de garantir l’unicité des éléments, d’effectuer des opérations ensemblistes, ou d’optimiser les recherches d’appartenance. Utilisez plutôt une liste quand l’ordre des éléments est important ou quand vous avez besoin d’accéder aux éléments par leur indice.

Utilisation de Set

Voici deux listes.


			fruits = ["pomme", "banane", "orange", "fraise", "cerise", 
			"poire", "abricot", "kiwi", "raisin", "mangue", 
			"ananas", "pêche", "citron", "framboise", "melon", 
			"pastèque", "myrtille", "mandarine", "prune", "figue", 
			"grenade", "litchi", "noix de coco", "groseille", "cassis"]

			food = ["farine", "oeuf", "jambon", "poireau", "pomme", 
			"riz", "poulet", "carotte", "oignon", "lait", 
			"beurre", "fromage", "sel", "poivre", "sucre", 
			"pain", "pâtes", "thon", "yaourt", "banane", 
			"orange", "chocolat", "huile", "miel", "noix de coco"]
		

Proposer une manière d’obtenir les listes suivantes :

  1. une liste avec tous les éléments de ces deux listes, sans doublon ;
  2. une liste avec tous les éléments communs à ces deux listes ;
  3. la liste food sans les éléments présents dans la liste fruits.

Bilan : Comparatif des 4 structures de données

Structure Syntaxe Ordonné ? Mutable ? Doublons ? Accès aux éléments
Liste (list) [1, 2, 3] Oui Oui Oui Par indice : L[0]
Tuple (tuple) (1, 2, 3) Oui Non Oui Par indice : T[0]
Dictionnaire (dict) {"a": 1} Oui Oui Clés uniques Par clé : D["a"]
Ensemble (set) {1, 2, 3} Non Oui Élements uniques Par itération / in