⚙️ Création de fonctions
- Fonction
- Paramètres
- Retour
- Portée
- DRY
- Commentaires
- Pile d'appels
- Trace
- Activités
- *Fonction en paramètre*
- *Récursivité*
Voici une fonction très simple :
def double(x):
return 2 * x
- Première ligne :
def double(x):defest un mot-clé qui indique que nous sommes en train de définir une fonction.doubleest le nom que nous avons décidé de donner à notre fonction.- Les parenthèses
()indiquent que c'est une fonction, et peuvent contenir des paramètres (ici un seul :x). xest un paramètre que nous avons décidé de nommerxet qui prend la valeur transmise lors de l'appel de la fonction.- Le
:à la fin de la ligne indique que le corps (le code) de la fonction commence à la ligne suivante.
- Toutes les lignes suivantes (ici seulement une ligne :
return 2 * x)- Elles font partie du corps de la fonction, et sont indentées d'une tabulation (touche
TAB) (espacement vers la droite).- En Python, l'indentation est essentielle pour délimiter les blocs de code.
returnest un mot-clé qui indique quelle valeur la fonction renvoie, ici le résultat de l'évaluation de l'expression2 * x.
- Elles font partie du corps de la fonction, et sont indentées d'une tabulation (touche
Une fois qu'une fonction est ainsi définie, on peut l'appeler, en lui transmettant une valeur pour x :
def double(x):
return 2 * x
double(5) # Appel de la fonction en lui passant la valeur 5
Pour appeler une fonction, on écrit son nom suivi de parenthèses contenant les valeurs à transmettre aux paramètres.
Lorsqu'une fonction est appelée, le code à l'intérieur de la fonction est exécuté, et la valeur spécifiée par return est renvoyée à l'endroit où la fonction a été appelée.
Ici, la fonction double est appelée en lui passant la valeur 5, ce qui signifie que x reçoit la valeur 5 à l'intérieur de la fonction.
La fonction calcule 2 * 5 et retourne 10.
Cependant, ici, la valeur retournée par la fonction est perdue.
👉 La fonction fonctionne… mais le résultat disparaît si on ne le stocke pas.
Il pourrait être plus utile de stocker cette valeur dans une variable :
resultat = double(5) # Appel de la fonction et stockage de la valeur retournée dans la variable 'resultat'
👉 Une fonction ne montre rien à l’écran : elle retourne une valeur que l'on peut ensuite utiliser.
Une fois une fonction définie, elle peut être appelée autant de fois que souhaité dans le code, en utilisant son nom suivi de parenthèses et des valeurs nécessaires :
resultat1 = double(5) # Appel de la fonction avec la valeur 5
resultat2 = double(10) # Appel de la fonction avec la valeur 10
resultat3 = double(2) # Appel de la fonction avec la valeur 2
Après l'exécution de ces lignes, resultat1 contiendra 10, resultat2 contiendra 20 et resultat3 contiendra 4.
Pour passer des valeurs à une fonction, il est aussi possible d’utiliser des variables :
a = 5
resultat = double(a) # Appel de la fonction en lui passant la valeur contenue dans la variable 'a'
Ici, la variable a contient la valeur 5, et lorsque nous appelons double(a), la fonction reçoit la valeur de a (c’est-à-dire 5) et retourne 10.
Les paramètres servent d'input à une fonction.
Les fonctions peuvent prendre un nombre variable de paramètres, y compris aucun paramètre du tout.
Voici 3 fonctions avec différents nombres de paramètres :
def nombre_pi():
return 3.14159
def carre(x):
return x * x
def puissance(base, exposant):
return base ** exposant
nombre_piest une fonction sans paramètre qui retourne la valeur de π.carreest une fonction avec un seul paramètrexqui retourne le carré dex.puissanceest une fonction avec deux paramètres,baseetexposant, qui retourne la base élevée à la puissance de l'exposant.
👉 Les paramètres sont séparés par des virgules.
Lorsqu'on appelle une fonction, il faut lui passer le même nombre de valeurs que de paramètres définis :
pi = nombre_pi() # Appel de la fonction sans paramètre
carre_de_5 = carre(5) # Appel de la fonction avec un paramètre (x=5)
Lorsque la fonction a plusieurs paramètres, l'ordre des valeurs transmises doit correspondre à l'ordre des paramètres définis dans la fonction :
resultat1 = puissance(2, 3) # Appel de la fonction avec base=2 et exposant=3 (retourne 8)
resultat2 = puissance(3, 2) # Appel de la fonction avec base=3 et exposant=2 (retourne 9)
Il est possible de définir une valeur par défaut pour un paramètre.
Si aucune valeur n'est fournie pour un paramètre lors de l'appel de la fonction, la valeur par défaut sera utilisée.
def puissance(base, exposant=2):
return base ** exposant
resultat1 = puissance(5) # retourne 25, car exposant prend la valeur par défaut 2
resultat2 = puissance(5, 3) # retourne 125, car exposant prend la valeur 3 fournie lors de l'appel
Comme dans cet exemple, une fonction peut posséder à la fois des paramètres avec des valeurs par défaut et des paramètres sans valeurs par défaut.
Lorsqu’on définit une fonction, il est courant de placer d’abord les paramètres obligatoires (ceux qui n’ont pas de valeur par défaut), suivis des paramètres facultatifs (ceux qui ont une valeur par défaut).
Cette organisation permet à l’utilisateur de la fonction de fournir les valeurs pour les paramètres essentiels en priorité, tout en ayant la liberté de spécifier uniquement les options supplémentaires s’il le souhaite.
def prix_total(prix, quantite=1, taxe=1.15):
return prix * quantite * taxe
# Exemples d'appels
total1 = prix_total(10) # 10 * 1 * 1.15 = 11.5
total2 = prix_total(10, 3) # 10 * 3 * 1.15 = 34.5
total3 = prix_total(10, 3, 1.2) # 10 * 3 * 1.2 = 36
total4 = prix_total(10, taxe=1.2) # 10 * 1 * 1.2 = 12
La dernière ligne de cet exemple est particulièrement intéressante à analyser :
- on fournit un prix (
10) ; - on ne fournit aucune valeur pour la quantité, la valeur par défaut sera utilisée (
1) ; - malgré l'absence de valeur pour la quantité, on peut fournir une valeur pour la taxe, en utilisant le nom du paramètre (
taxe=1.2).
Les valeurs retournées servent d'output à la fonction.
Une fonction peut retourner plusieurs valeurs, séparées par des virgules :
def diviser(a, b):
quotient = a / b
reste = a % b
return quotient, reste
q, r = diviser(10, 3)
Dans cet exemple, la fonction diviser retourne à la fois le quotient et le reste de la division.
Ces valeurs sont ensuite assignées à deux variables distinctes (q et r), séparées par une virgule lors de l'appel de la fonction.
L'exécution de la fonction s'arrête dès qu'une instruction return est rencontrée.
Pour cette raison, l'instruction return est généralement placée à la toute fin d'une fonction.
Cependant, une fonction peut comporter plusieurs instructions return à différents endroits du code, notamment dans des conditions différentes.
Nous verrons les conditions plus tard, mais voici tout de même un exemple simple :
def plus_grand(a, b):
if a > b:
return a
else:
return b
Une fonction peut ne pas avoir d'instruction return.
Dans ce cas, elle retourne automatiquement la valeur None.
Par exemple, la fonction ci-dessous affiche simplement une salutation à la console sur 2 lignes :
def salutation(nom, age):
print("Bonjour")
print(f"Je m'appelle {nom}, j'ai {age} ans")
C'est pareil comme si return None était ajouté à la fin de la fonction :
def salutation(nom, age):
print("Bonjour")
print(f"Je m'appelle {nom}, j'ai {age} ans")
return None
Par exemple, le code ci-dessous affichera None :
resultat = salutation("Gustave", 32) # Affiche la salutation, puis stocke `None` dans `resultat`
print(resultat) # Affiche la valeur de `resultat`, qui est `None`
print(salutation("Sciel", 32)) # Affiche la salutation, puis affiche la valeur de retour `None`
Pour cette raison, n'affichez jamais le résultat d'une fonction qui ne retourne rien!
Cela affichera toujours None, ce qui n'a aucun sens.
# bonne utilisation :
salutation("Gustave", 32) # Affiche la salutation
# mauvaise utilisation :
print(salutation("Gustave", 32)) # Affiche la salutation, puis `None`
On peut aussi définir une fonction qui construit et retourne une chaîne de caractères, sans jamais afficher quoi que ce soit à l'écran :
def salutation(nom, age):
return f"Bonjour\nJe m'appelle {nom}, j'ai {age} ans."
message = salutation("Gustave", 32) # Stocke la chaîne de caractères retournée par la fonction
# dans la variable 'message'
print(message) # Affiche le message stocké dans la variable 'message'
👉 Si jamais vous êtes confus.e entre print et return, lisez cette section.
La portée d'une variable détermine où cette variable peut être accédée dans le code.
En Python, il existe deux types principaux de portée : locale et globale.
Une variable définie en dehors d’une fonction est globale.
Elle peut être utilisée dans tout le programme, y compris à l’intérieur des fonctions,
sauf si une variable du même nom est définie localement (cette situation est montrée plus bas).
x = 10 # Variable globale
def ma_fonction():
print(f"Valeur de x dans la fonction : {x}") # x est accessible ici
ma_fonction()
print(f"Valeur de x en dehors de la fonction : {x}")
Une variable définie à l’intérieur d’une fonction est locale. Elle n’existe que dans cette fonction.
Les paramètres d'une fonction ont une portée locale également (ce sont comme des variables locales).
def ma_fonction(z): # z a une portée locale
y = 5 # Variable locale
print(f"Valeur de y dans la fonction : {y}")
print(f"Valeur de z dans la fonction : {z}")
ma_fonction(5)
ma_fonction(4)
# print(y) # Erreur : y n'existe pas en dehors de la fonction
# print(z) # Erreur : z n'existe pas en dehors de la fonction
Deux variables ne peuvent pas avoir le même nom dans le même espace de portée, sinon c'est la même variable!
Cependant, si 2 variables ont le même nom mais dans des espaces de portée différents (une globale et une locale), la version locale prend le dessus dans sa portée. Elle cache la variable globale, mais ne la modifie pas.
x = 10 # Variable globale
def ma_fonction():
x = 5 # Nouvelle variable locale, différente de la globale
print(f"Valeur locale de x : {x}") # Affiche 5
ma_fonction()
print(f"Valeur globale de x : {x}") # Affiche 10
👉 Dans cet exemple, la variable x à l’intérieur de la fonction est indépendante de celle à l’extérieur.
Elles ont le même nom, mais ce sont 2 variables différentes. Lorsque vous faites la trace, vous devez prévoir 2 colonnes!
En programmation, on suit souvent le principe DRY (Don't Repeat Yourself). Si vous remarquez que vous copiez-collez du code en changeant seulement quelques valeurs, il est temps de créer une fonction!
Prenons un exemple en physique spatiale où l'on calcule et affiche le poids d'un spationaute dont la masse est de 75 kg sur différents astres (rappel : le poids est une force en newtons (N) calculée par la formule ).
masse_spationaute = 75
# Calcul et affichage pour la Terre
poids_terre = masse_spationaute * 9.81
print(f"Sur l'astre Terre, le poids est de {poids_terre:.2f} N.")
# Calcul et affichage pour Mars
poids_mars = masse_spationaute * 3.71
print(f"Sur l'astre Mars, le poids est de {poids_mars:.2f} N.")
# Calcul et affichage pour la Lune
poids_lune = masse_spationaute * 1.62
print(f"Sur l'astre Lune, le poids est de {poids_lune:.2f} N.")
Ici, les lignes de calcul et d'affichage sont répétées trois fois. C'est l'occasion idéale pour extraire le tout dans des fonctions.
Dans cet exemple, on va créer 2 fonctions : une pour faire le calcul du poids, et une pour faire l'affichage, qui appellera la première fonction pour faire le calcul.
Commençons par la fonction de calcul.
Fonction 1 : calcul du poids
Étape 1 : Identifier les éléments qui changent
Regardez attentivement les blocs de code répétés et identifiez ce qui varie d'un bloc à l'autre. Ces variables deviendront les paramètres de notre fonction.
Dans notre exemple, voici ce qui change :
- La valeur de la gravité (
9.81,3.71,1.62) - La masse pourrait aussi changer si on veut être plus flexible (
75).
Ce qui reste identique, c'est l'opération mathématique (la multiplication) et la phrase affichée dans le print.
Étape 2 : Extraire la fonction de calcul (avec retour)
Commençons par isoler le calcul mathématique. On veut créer une fonction qui reçoit les variables nécessaires, effectue le calcul, et nous redonne le résultat.
- Paramètres : la masse (
m) et la gravité (g). - Variable locale : le résultat du calcul (
poids). Elle n'existera que le temps de ce calcul. - Valeur de retour : le
poidscalculé (le résultat de la multiplication).
def calculer_poids(m, g):
poids = m * g
return poids
👉 Cette fonction est pure : elle fait un calcul et retourne une valeur, sans rien afficher à l'écran.
Résultat intermédiaire : intégration du calcul
Nous avons maintenant isolé la logique mathématique. Le script principal appelle maintenant notre fonction calculer_poids au lieu de réécrire la formule à chaque fois.
def calculer_poids(m, g):
poids = m * g
return poids
masse_spationaute = 75
# On utilise maintenant la fonction pour obtenir les valeurs
p_terre = calculer_poids(masse_spationaute, 9.81)
print(f"Sur l'astre Terre, le poids est de {p_terre:.2f} N.")
p_mars = calculer_poids(masse_spationaute, 3.71)
print(f"Sur l'astre Mars, le poids est de {p_mars:.2f} N.")
p_lune = calculer_poids(masse_spationaute, 1.62)
print(f"Sur l'astre Lune, le poids est de {p_lune:.2f} N.")
👉 Observation : Le calcul est maintenant isolé dans une fonction (si on veut changer la précision, on ne le fait qu'à un seul endroit). Cependant, on remarque que les instructions print sont encore très répétitives. On va donc extraire une deuxième fonction pour gérer l'affichage, et qui appellera la fonction de calcul pour obtenir le poids.
Fonction 2 : affichage du poids
Étape 1 : Identifier ce que la fonction doit afficher
Regardez les lignes d'affichage et identifiez ce qui varie d'une ligne à l'autre.
Dans notre exemple, la phrase affichée contient :
- le nom de l'astre (
"Terre","Mars","Lune") - le poids calculé à partir de la masse et de la gravité
La structure de la phrase reste toujours la même.
Étape 2 : Extraire la fonction d'affichage (sans retour)
Nous allons créer une deuxième fonction qui gère le texte affiché, et qui appellera notre première fonction pour obtenir le poids.
- Paramètres : le nom de l'astre (
nom_astre), la masse (m) et la gravité (g). - Variable locale : le poids calculé par l'appel de fonction (
poids_calcule). - Valeur de retour : aucune (elle ne fera que des
print).
def afficher_poids_astre(nom_astre, m, g):
# Appel à l'autre fonction pour faire le calcul
poids_calcule = calculer_poids(m, g)
# Affichage du résultat
print(f"Sur l'astre {nom_astre}, le poids est de {poids_calcule:.2f} N.")
Résultat final : intégration de l'affichage
Une fois nos deux fonctions créées, notre script principal devient beaucoup plus court et plus lisible!
# --- Définition des fonctions ---
def calculer_poids(m, g):
poids = m * g
return poids
def afficher_poids_astre(nom_astre, m, g):
poids_calcule = calculer_poids(m, g)
print(f"Sur l'astre {nom_astre}, le poids est de {poids_calcule:.2f} N.")
# --- Script principal ---
masse_spationaute = 75
afficher_poids_astre("Terre", masse_spationaute, 9.81)
afficher_poids_astre("Mars", masse_spationaute, 3.71)
afficher_poids_astre("Lune", masse_spationaute, 1.62)
👉 Si demain on veut changer la phrase affichée ou modifier le format d’affichage, on n’aura qu’à modifier un seul endroit dans le code au lieu de trois.
Il est possible d'ajouter des commentaires dans le code pour expliquer ce que fait une fonction ou pour donner des indications sur son utilisation.
Les commentaires sont ignorés par l'interpréteur Python et n'affectent pas l'exécution du code.
Ils sont utiles pour rendre le code plus lisible et compréhensible pour les autres développeurs (et pour vous-même dans le futur!).
En Python, les commentaires sont créés en utilisant le symbole # pour commenter une ligne entière ou une partie de ligne.
Pour documenter une fonction, il est possible d'utiliser les triples guillemets """ (ou ''').
Ce type de documentation se nomme un docstring et est toujours placé au début de la définition d'une fonction.
Créer une fonction sans mettre une documentation est une mauvaise pratique!
Par exemple :
# Ceci est un commentaire sur une seule ligne
def energie_cinetique(masse, vitesse): # Ceci est un commentaire en fin de ligne
"""
Calcule l'énergie cinétique d'un objet en mouvement.
Paramètres :
masse (float) : Masse de l'objet en kilogrammes (kg)
vitesse (float) : Vitesse de l'objet en mètres par seconde (m/s)
Retourne :
float : Énergie cinétique en Joules (J)
Exemple d'utilisation :
energie_cinetique(10, 5)
"""
nb_joute = 0.5 * masse * pow(vitesse,2)
return nb_joute
# Exemple avec différentes valeurs
objet1 = energie_cinetique(2, 3) # Masse de 2 kg, vitesse de 3 m/s
objet2 = energie_cinetique(5, 7) # Masse de 5 kg, vitesse de 7 m/s
# Affichage des résultats
print("Énergie cinétique de l'objet 1 :", objet1, "Joules")
print("Énergie cinétique de l'objet 2 :", objet2, "Joules")
Les lignes 3 à 19 montrent un exemple de commentaire de bloc utilisant des triples guillemets pour expliquer en détail la fonction energie_cinetique.
Lorsqu'une fonction possède une documentation sous forme de docstring, l'IDE vous permet de visualiser cette documentation rapidement.
Par exemple, si vous placez votre souris sur l'un des deux appels de fonction energie_cinetique, la documentation sera affichée.

Les docstrings servent qu'à la documentation et elles sont ignorées par Python lors de l'exécution du code.
Il en est de même pour les lignes 1, 23 et 27 qui sont des commentaires sur une seule ligne, ainsi que tout ce qui se trouve à droite du # à la ligne 2.
Les fonctions peuvent appeler d'autres fonctions.
Cela est utile pour décomposer des tâches complexes en sous-tâches plus simples.
def ajouter(x, y):
return x + y
def multiplier(x, y):
return x * y
def calculer_expression(a, b, c):
somme = ajouter(a, b)
resultat = multiplier(somme, c)
return resultat
valeur = calculer_expression(2, 3, 4)
print(valeur) # Affiche 20
Dans cet exemple, la fonction calculer_expression appelle les fonctions ajouter et multiplier pour
effectuer une série de calculs.
Lorsque l’on démarre un programme Python, une pile d'appels est automatiquement créée.
Cette structure de données, généralement invisible sauf en cas d’erreur ou via des outils de débogage,
permet à Python de suivre l’ordre d’exécution des fonctions et de conserver l’état des variables locales.
🔁 Fonctionnement de la pile
- Lorsqu’une instruction appelle une fonction, celle-ci est empilée au sommet de la pile d'appels.
- Python exécute une seule fonction à la fois, toujours celle qui se trouve en haut de la pile.
- Une fois l’exécution terminée, la fonction est retirée de la pile.
- La fonction suivante au sommet devient alors la nouvelle fonction active, que Python exécute immédiatement.
📚 Principe LIFO
La pile d'appels suit le principe LIFO (Last In, First Out) :
- La dernière fonction ajoutée est la première à être exécutée, et la première à être retirée.
- Ce mécanisme permet à Python de gérer les appels imbriqués de fonctions, et de revenir à l’état précédent une fois qu’une fonction a terminé son exécution.
def fonction_a():
print("Fonction A")
fonction_b()
def fonction_b():
print("Fonction B")
fonction_a()
Dans cet exemple, lorsque fonction_a est appelée, elle appelle fonction_b, qui est ajoutée à la pile d'appels.
Une fois que fonction_b a terminé son exécution, le contrôle revient à fonction_a.
Le débogueur permet d'inspecter tous les détails de la pile d'appels.
Copiez le code ci-dessous dans un script, mettez un point d'arrêt sur la ligne 12 (valeur = ...) et exécutez le script en mode débogage.
def ajouter(x, y):
return x + y
def multiplier(x, y):
return x * y
def calculer_expression(a, b, c):
somme = ajouter(a, b)
resultat = multiplier(somme, c)
return resultat
valeur = calculer_expression(2, 3, 4)
print(valeur) # Affiche 20
Pour entrer dans les fonctions appelées en mode débogage, il faut utiliser le bouton Step Into .
Lorsque vous entrez dans la fonction calculer_expression, vous voyez :
- cette fonction s'ajouter au sommet de la pile d'appels (à gauche),
- les paramètres
a,betcapparaître dans la liste des variables (à droite) :

Si vous appuyez de nouveau sur , vous entrez dans la fonction
ajouter.
Vous pouvez voir que la fonction ajouter est maintenant au sommet de la pile d'appels, et les paramètres x et y sont affichés dans la liste des variables :

Remarquez que les paramètres a, b et c de calculer_expression ne sont plus visibles,
car vous êtes maintenant dans la fonction ajouter et a, b et c sont hors de portée.
Vous pouvez cliquer sur le nom de la fonction calculer_expression dans la pile d'appels pour revenir à cette fonction et voir ses paramètres à nouveau :

Lorsque vous faites la trace d'exécution d'un code qui contient des appels de fonctions,
il est nécessaire de noter la pile d'appels et les variables locales à chaque étape
comme dans l'exemple de trace disponible ici.
Le débogueur est alors le meilleur outil pour valider votre trace... sauf pour le volet papier de l'examen!
🕵️♂️ Trace d’exécution avec des fonctions
Voici un exemple de trace d'exécution contenant des appels de fonctions
Code
Si on a le code suivant :
def carre(n):
return n * n
def affiche_resultat():
a = 2
b = 3
total = carre(a) + carre(b)
print(f'La somme des carrés est : {total}')
z = 42
affiche_resultat()
Trace
Explications détaillées de la trace et de sa validation
On aura la trace suivante :
| portée globale | portée de affiche_resultat | portée de carre | |||||||
|---|---|---|---|---|---|---|---|---|---|
| #ligne | z | a | b | total | n | affichage | pile d'appels | fonction appelée | valeur retournée |
| 10 | 42 | ||||||||
| 11 | 42 | affiche_resultat() | |||||||
| 5 | 42 | 2 | affiche_resultat | ||||||
| 6 | 42 | 2 | 3 | affiche_resultat | |||||
| 7 | 42 | 2 | 3 | affiche_resultat | carre(2) | ||||
| 2 | 42 | 2 | carreaffiche_resultat | 4 | |||||
| 7 | 42 | 2 | 3 | affiche_resultat | carre(3) | ||||
| 2 | 42 | 3 | carreaffiche_resultat | 9 | |||||
| 7 | 42 | 2 | 3 | 13 | affiche_resultat | ||||
| 8 | 42 | 2 | 3 | 13 | La somme des carrés est : 13 | affiche_resultat | |||
Explications
On indique la portée des variables en les regroupant par portée (première ligne).
Ici, on a la portée globale (variables définies en dehors de toute fonction), la portée de affiche_resultat et la portée de carre.
On remarque que la variable n n'existe que dans la portée de la fonction carre, et que les variables a, b et total n'existent que dans la portée de la fonction affiche_resultat.
La variable z est dans la portée globale, c'est pourquoi elle est visible dans toute la trace.
On indique aussi la fonction appelée (avant son exécution) et la valeur qu'elle retourne (après son exécution) (dernières colonnes).
On indique aussi la pile d'appels, c'est-à-dire les fonctions en cours d'exécution (3e colonne à partir de la droite).
Ce genre de code fait souvent appel à plusieurs appels de fonctions imbriqués.
On peut donc visualiser la pile d'appels comme un empilement temporaire où chaque appel s’ajoute en haut de la pile, et disparaît dès qu’il est terminé.
Voici maintenant un petit exemple qui démontre l'importance de spécifier la portée des variables, où on a une variable x dans la portée globale et une autre variable nommée x dans la portée de la fonction ma_fonction.
Code
def ma_fonction(y):
x = 3 + y
print(f'dans ma_fonction, x vaut {x}')
x = 5
print(f'dans main, x vaut {x}')
ma_fonction(100)
print(f'dans main, x vaut toujours {x}')
Trace
| portée globale | portée de ma_fonction | ||||||
|---|---|---|---|---|---|---|---|
| #ligne | x | y | x | affichage | pile d'appels | fonction appelée | valeur retournée |
| 5 | 5 | ||||||
| 6 | 5 | dans main, x vaut 5 | |||||
| 7 | 5 | ma_fonction(100) | |||||
| 2 | 5 | 100 | 103 | ma_fonction | |||
| 3 | 5 | 100 | 103 | dans ma_fonction, x vaut 103 | ma_fonction | ||
| 8 | 5 | dans main, x vaut toujours 5 | |||||
On voit que la variable x dans la portée de ma_fonction n'a rien à voir avec la variable x dans la portée globale.
Cette dernière n'est pas modifiée par l'exécution de ma_fonction, malgré le fait qu'on ait une instruction x = 3 + y dans ma_fonction et que la variable globale x soit aussi accessible dans ma_fonction (car elle est globale).
La raison est que l'instruction x = 3 + y crée une nouvelle variable x dans la portée de ma_fonction, qui masque la variable globale x.
Si on voulait modifier la variable globale x dans ma_fonction, il faudrait utiliser le mot-clé global (ce qui est déconseillé en général).
Pour favoriser une progression efficace, ce cours adopte une approche de classe inversée. Ce modèle déplace l'acquisition théorique hors de la classe pour transformer les heures de rencontre en véritables séances d'application pratique.
-
À la maison (Exploration et Appropriation) : Vous êtes le maître de votre rythme. Ces activités vous permettent de découvrir les nouveaux concepts et tester les exemples au moment où vous êtes le plus concentré. Si une notion vous semble obscure, n'hésitez pas à poser des questions à votre prof au fur et à mesure.
-
En classe (Application et Consolidation) : Le temps en présentiel est une ressource précieuse. Nous l'utilisons pour nous attaquer aux défis plus complexes et aux exercices de programmation qui demandent une mobilisation active de vos connaissances. C’est le moment idéal pour obtenir une rétroaction immédiate, collaborer avec vos pairs et bénéficier de l'accompagnement de votre enseignant au moment où vous en avez le plus besoin.
- 🏠 Activités à faire à la maison
- 🏫 Activités à faire en classe
🧠 Auto-validation des connaissances
Ce formulaire sert à vérifier votre compréhension des éléments les plus importants de la rencontre R03. Ne faites ce questionnaire que lorsque vous vous sentez en très bonne maîtrise de la matière. En classe, dès votre arrivée, vous aurez un questionnaire très similaire, évalué sommativement, à compléter sans accès à aucune documentation.
Ne pas remplir le formulaire diminue fortement vos chances de réussir l’évaluation sommative en début de rencontre.
Assurez-vous de bien comprendre toutes les notions derrière chaque question.
✏️ Exercices de compréhension
Pour le premier exercice de cette section, vous pouvez écrire vos réponses sur papier ou vous pouvez créer un fichier markdown.
Pour les autres exercices, vous pouvez imprimer ce tableau de trace d'exécution pour réaliser les traces.
Il n’y a aucune trace à rédiger pour ce numéro.
Soit le code suivant :
def calcul_complexe(a, b, c):
x = a ** b
y = c * (b - a)
return a ** b + c * (b - a)
x = 5
calcul_complexe(3, 5, 7)
print(x)
Combien vaut x dans calcul_complexe? (ligne 2)
Combien vaut x dans le script principal? (ligne 6)
S’agit‑il de la même variable 𝑥 ou de deux variables distinctes? Expliquez.
Sans exécuter le code, qu'affichera le programme ci-dessous?
En suivant cette recette, faites la trace de l'exécution de ce code :
En situation d'examen, ce type de question serait à faire sans l'ordinateur.
def fonction_lineaire(a, x, b) :
print("appel de la fonction")
resultat = a * x + b
return resultat
x = 5
y = 10
z = 2
r1 = fonction_lineaire(x, z, y) # Attention à l'ordre des paramètres
x = x - 3
r2 = fonction_lineaire(x, y, z)
print(r1, r2)
Validez votre trace en exécutant le code dans un script en mode débogage.
En suivant cette recette, faites la trace de l'exécution de ce code :
def ajoute_cinq(x):
print("appel de ajoute_cinq")
return x + 5
def carre(x):
print("appel de carre")
return x * x
def calcul_final(a, b):
print("appel de calcul_final")
return carre(a) + ajoute_cinq(b)
x = 2
y = 3
resultat = calcul_final(x, y)
print(resultat)
Validez votre trace en l'exécutant en mode débogage.
🔨 Exercices de création
Pour chacun des exercices de cette section, vous devez créer des fichiers .py avec un nom significatif.
Écrivez une fonction celsius_vers_fahrenheit(celsius) qui reçoit une température en °C et retourne sa valeur
en °F grâce à la formule
Tester votre fonction à l'aide des instructions ci-dessous :
print(celsius_vers_fahrenheit(0)) # 32.0
print(celsius_vers_fahrenheit(100)) # 212.0
Écrivez une fonction aire_rectangle(longueur, largeur) qui calcule et retourne l’aire d’un rectangle.
Puis, appelez la fonction pour afficher l’aire d’un rectangle de 5 m 3 m. Par exemple :
print(f"L'aire d'un rectangle de 5 x 3 est : {aire_rectangle(5,3)}") # L'aire d'un rectangle de 5 x 3 est : 15
-
Écrivez une fonction
aire_rectangle(longueur, largeur)qui calcule et retourne l’aire d’un rectangle. -
Écrivez une deuxième fonction
volume_prisme(longueur, largeur, hauteur)qui calcule et retourne le volume d’un prisme rectangulaire.
👉 Cette deuxième fonction doit réutiliser (appeler) votre fonction aire_rectangle pour calculer la base du prisme avant de la
multiplier par la hauteur.
- Testez vos fonctions en affichant :
- l’aire d’un rectangle de
- le volume d’un prisme de
Objectif : pratiquer la décomposition d’un problème en plusieurs fonctions réutilisables.
Pour cet exercice, vous devez créer un module (un fichier .py) et un script (un autre fichier .py).
Référez-vous à la recette sur les modules pour savoir comment faire.
Le module doit contenir une fonction, et le script doit importer ce module pour utiliser la fonction.
- Dans le module, nommé
sciences_nature, écrivez une fonction permettant de calculer l'énergie cinétique en utilisant la formule :
où est la masse et la vitesse.
- Dans le script (un autre fichier
.py) :- importez le module,
- appelez votre fonction avec différentes valeurs,
- affichez les résultats.
Dans le même module créé à l'exercice précédent, ajoutez une fonction permettant de calculer le poids en utilisant la loi de la gravitation universelle de Newton dont la formule est :
où :
- (constante gravitationnelle),
- et sont les masses en kg,
- est la distance en mètres.
Dans un script Python (ça peut être le même qu'à l'exercice précédent) :
- importez le module,
- appelez votre fonction avec différentes valeurs,
- affichez les résultats.
Écrivez une fonction dire_bonjour() qui affiche simplement le message "Bonjour!" dans la console.
Ensuite, modifiez la fonction pour qu’elle prenne un paramètre prenom et affiche :
Bonjour, Alice!
lorsqu’on appelle dire_bonjour("Alice").
Le code ci-dessous affiche un rapport concernant des expérimentations réalisées par une entreprise pharmaceutique de confiance.
Beaucoup d'instructions servant a afficher des données sont très similaires et sont utilisées dans le même ordre.
En programmation, c'est un signe qu'on doit créer une fonction!
Améliorez ce code! Définissez une fonction et appelez-la.
print("Rapport - Résultats des expériences Umbrella Corporation")
print("Échantillon : Virus-T")
print("Température : 37.0°C")
print("pH : 6.2")
print("Mutation Cellulaire : Stable")
print("Contagiosité : Élevée")
print("---------------")
print("Échantillon : Virus-G")
print("Température : 39.5°C")
print("pH : 5.8")
print("Mutation Cellulaire : Instable")
print("Capacité Régénérative : Extrême")
print("---------------")
print("Échantillon : Uroboros")
print("Température : 40.0°C")
print("pH : 7.0")
print("Mutation Cellulaire : Agressive")
print("Compatibilité Hôte : Faible")
print("---------------")
print("Fin du rapport. Données classifiées - Niveau d'accès : Alpha")
Lorsqu'on exécute la version originale du code, ou votre version améliorée, on devrait observer le même résultat dans la console.
Le code ci-dessous permet de calculer la densité en kg/m3 de 3 échantillons pour lesquels nous possédons une masse en g et un volume en cm3.
Le calcul de la densité nécessite plusieurs instructions. Simplifiez ce code en créant une fonction pour calculer la densité en kg/m3 et appelez-la.
⚠️ Il est INTERDIT de modifier les instructions 1 à 7 et 20 à 23, inclusivement.
#Variables contenants les données de 3 échantillons
echantillon1_masse_en_g = 135
echantillon1_volume_cm3 = 84
echantillon2_masse_en_g = 270
echantillon2_volume_cm3 = 151
echantillon3_masse_en_g = 92
echantillon3_volume_cm3 = 113
#Calcul des densitées pour les 3 échantilllons
echantillon1_masse_en_kg = echantillon1_masse_en_g / 1000
echantillon1_volume_m3 = echantillon1_volume_cm3 / 1_000_000
echantillon1_densite = echantillon1_masse_en_kg / echantillon1_volume_m3
echantillon2_masse_en_kg = echantillon2_masse_en_g / 1000
echantillon2_volume_m3 = echantillon2_volume_cm3 / 1_000_000
echantillon2_densite = echantillon2_masse_en_kg / echantillon2_volume_m3
echantillon3_masse_en_kg = echantillon3_masse_en_g / 1000
echantillon3_volume_m3 = echantillon3_volume_cm3 / 1_000_000
echantillon3_densite = echantillon3_masse_en_kg / echantillon3_volume_m3
#Affichage des résultats
print("Densité 1 :", round(echantillon1_densite, 2), "kg/m3")
print("Densité 2 :", round(echantillon2_densite, 2), "kg/m3")
print("Densité 3 :", round(echantillon3_densite, 2), "kg/m3")
Lorsqu'on exécute la version originale du code, ou votre version améliorée, on devrait observer le même résultat dans la console.
Dans la documentation du module time (doc), on trouve la fonction time.monotonic_ns().
Cette fonction permet d'obtenir le temps en nanosecondes depuis le lancement de l'application. À chaque appel de la fonction, le temps retourné est toujours de plus en plus grand. Testez le code ci-dessous :
import time
print(f"Temps moment A: {time.monotonic_ns()}")
print(f"Temps moment B: {time.monotonic_ns()}")
print(f"Temps moment C: {time.monotonic_ns()}")
Maintenant que vous comprennez ce que retourne cette fonction, observez son utilisation ci-dessous :
import time
def calcul_intensif():
'''
N'essayez VRAIMENT pas de comprendre le code de cette fonction!
Vous allez être en mesure de le comprendre à partir du cours sur les boucles.
La seule chose qui est importante de savoir, c'est que cette fonction prend un
certain temps pour s'exécuter (1 à 2 secondes en moyenne).
'''
total = 0
for i in range(1, 20000000): # 20 millions d'itérations
total += i ** 0.5
return total
tempA = time.monotonic_ns()
calcul_intensif()
tempB = time.monotonic_ns()
mystere = tempB - tempA # Expression intéressante
print(mystere)
Question
✏️ Selon vous, en observant les instructions 15 à 19, que représente le nombre contenu dans la variable mystere?
Instructions pour vous aider dans votre réflexion
- Exécutez le programme tel quel et notez la valeur affichée.
- À la ligne 11, modifiez le nombre 20 000 000 pour 40 000 000.
- Réexécutez le programme et notez le nouveau résultat.
- Expliquez la différence entre les 2 résultats obtenus.
✏️ Exercices de compréhension
Pour les exercices de cette section, vous pouvez écrire vos réponses sur papier ou vous pouvez créer un fichier markdown.
Vous allez exécuter un script auquel on ne comprend rien pour l'instant.
Le but est de se familiariser avec le débogueur.
Tout d'abord, créez un nouveau fichier points_arrets.py et copiez-y le code suivant :
# ceci est une fonction, nous verrons au prochain cours comment les écrire
# l'indentation de la 2e ligne est importante (les espaces au début de la ligne)
def calcul_complexe(a, b, c):
return a + c * (b - a)
print("Arrêter le temps, c'est impossible Marty.")
a = calcul_complexe(3, 5, 6)
b = calcul_complexe(a, 6, 4)
c = calcul_complexe(b, a, b)
a = calcul_complexe(a, a, a)
print("Le resultat est ", calcul_complexe(a, b, c))
On veut savoir quelles sont les valeurs de a au fil de l'exécution du script.
- Faites d'abord une trace de ce script, en essayant de deviner les valeurs de
aà chaque étape. - Ensuite, placez un point d'arrêt sur la ligne 7 et exécutez le script en mode débogage.
- Avancez ligne par ligne et observez les valeurs de
a,betcdans la fenêtre de débogage. - Notez les valeurs de
aà chaque étape et comparez-les avec votre trace. - Si vous avez des différences, essayez de comprendre pourquoi.
- N'hésitez pas à demander de l'aide si vous avez des difficultés.
Cet exercice doit d'abord être réalisé sur papier (comme à l'examen) ou dans un bloc-notes simple.
L'utilisation de PyCharm est autorisée uniquement pour la validation finale (ce que vous ne pourrez pas faire à l'examen pour ce type de question sur papier).
Écrivez un programme qui calcule la force nécessaire () pour maintenir un objet en mouvement circulaire (comme une voiture dans un virage).
Formule :
(Note : est la masse en kg, la vitesse en m/s et le rayon du virage en mètres)
Contraintes techniques :
- Définissez une fonction nommée
calculer_force_centripeterecevant 3 paramètres : la masse, la vitesse et le rayon. - La fonction doit retourner la valeur calculée (elle ne doit pas faire de print elle-même).
- Appelez la fonction, récupérez la valeur retournée dans une variable, puis affichez le résultat final avec deux décimales.
Exemple attendu (pour , , ) :
Paramètres : 1200 kg, 25 m/s, 50 m
La force centripète résultante est de 15000.00 N
🎯 Solutions des exercices
Un solutionnaire possible pour chaque exercice est disponible en format vidéo avec des explications. Vous pouvez vous en servir pour comparer votre solution avec une solution possible jugée optimale et vous débloquer après un long moment bloqué sur un exercice (ex. 20 minutes) et après avoir utilisé le dégogueur pour essayer par vous-même de trouver le problème.
Il est tout à fait normal que certains problèmes demandent du temps, de la réflexion, et parfois même un peu de frustration. C’est précisément dans ces moments d’effort que l’apprentissage s’ancre réellement.
Consulter un solutionnaire avant d’avoir tenté l’exercice par soi-même, ou le parcourir trop rapidement, revient à court-circuiter le processus d’apprentissage. Ce n’est pas simplement contre-productif, c’est pédagogiquement désastreux.
En sautant l’étape de la réflexion personnelle, on prive son cerveau de l’occasion de construire des connexions durables. Et à force de répéter ce réflexe, on risque de passer à côté des compétences essentielles, ce qui peut mener à des échecs plus tard, même si tout semble facile sur le moment. Alors oui, prenez le temps. L’erreur fait partie du jeu. C’est en cherchant, en tâtonnant, en doutant, qu’on devient réellement compétent. Le solutionnaire doit être un outil de validation, pas un raccourci.
🔨 Solution des exercices de création :
Le passage de fonctions en paramètre est une notion plus avancée qui dépasse le cadre de ce cours.
Elle n'est pas obligatoire pour réussir le cours, mais il s'agit d'un concept intéressant à explorer si vous êtes curieu.x.se.
Vous pouvez choisir de ne pas lire cette section si vous n'êtes pas assez à l'aise ou si vous êtes déjà assez mêlé.e comme ça!
Vous pouvez utiliser ou non cette notion pour réduire la quantité de code de la section Comparaison des performances du TP1.
En Python, une fonction est un objet comme un autre (comme un int ou une str). Cela signifie que vous pouvez :
- Stocker une fonction dans une variable.
- Passer une fonction en paramètre à une autre fonction.
Imaginez un instrument de laboratoire qui lit une température en Celsius, mais qui doit pouvoir afficher le résultat dans différentes unités selon l'expérience en cours.
On commence par définir des fonctions simples qui font une seule tâche : la conversion.
def vers_kelvin(celsius):
return celsius + 273.15
def vers_fahrenheit(celsius):
return (celsius * 9/5) + 32
C'est ici que la magie opère. On crée une fonction qui accepte une valeur ET une autre fonction comme paramètre.
# Le paramètre 'transformation' recevra une fonction (voir l'appel de cette fonction à l'étape #3)
def afficher_resultat_labo(valeur_c, transformation):
# On utilise le paramètre comme s'il s'agissait d'une fonction locale
resultat = transformation(valeur_c)
print(f"L'instrument affiche : {resultat}")
Lors de l'appel, on passe la fonction de conversion sans les parenthèses.
Si vous mettez des parenthèses, vous passez le résultat de la fonction au lieu de la fonction elle-même!
temp_piece = 21.0
# On passe 'vers_kelvin' comme un simple objet
print("--- Mode Scientifique ---")
afficher_resultat_labo(temp_piece, vers_kelvin)
# On change le comportement simplement en passant une autre fonction
print("--- Mode Américain ---")
afficher_resultat_labo(temp_piece, vers_fahrenheit)
Cela permet de créer une fonction "générique" (afficher_resultat_labo) qui ne change jamais, même si vous ajoutez 50 nouveaux types de conversions plus tard. On sépare la logique d'affichage de la logique de calcul.
La récursivité est une notion plus avancée qui dépasse le cadre de ce cours.
Elle n'est pas obligatoire pour réussir le cours, mais il s'agit d'un concept intéressant à explorer si vous êtes curieu.x.se.
Vous pouvez choisir de ne pas lire cette section si vous n'êtes pas assez à l'aise ou si vous êtes déjà assez mêlé.e comme ça!
La récursivité est une technique de programmation où une fonction s'appelle elle-même pour résoudre un problème.
Cela peut être utile pour résoudre des problèmes qui peuvent être décomposés en sous-problèmes similaires.
Voici un exemple simple de fonction récursive qui calcule la factorielle d'un nombre :
(note : le code contient un if que l'on n'a pas encore vu, mais c'est pour illustrer la récursivité)
def factorielle(n):
if n == 0 or n == 1: # Cas de base
return 1
else:
return n * factorielle(n - 1) # Appel récursif
print(factorielle(5)) # Affiche 120
Dans cet exemple, la fonction factorielle s'appelle elle-même avec un paramètre réduit jusqu'à atteindre le cas de base (0 ou 1), où elle retourne 1.
La pile d'appels se remplit à chaque appel récursif, puis se vide lorsque les appels sont résolus.
Si vous êtes curieu.x.se vous pouvez exécuter ce code en mode débogage et observer la pile d'appels pour voir comment elle évolue avec chaque appel récursif.