Définir une fonction personnelle⚓︎
Nous pouvons définir des fonctions pour notre propre usage ou pour la communauté de programmeurs.
Fonctions sans paramètre ni valeur de retour⚓︎
Supposons que je veuille écrire la même phrase à différents endroits d'un programme :
...
print("Vous trouverez plus d'informations en vous rendant sur notre site internet")
...
...
print("Vous trouverez plus d'informations en vous rendant sur notre site internet")
...
alors je peux remarquer deux problèmes :
- c'est clairement une perte de temps de devoir retaper le même texte (oui, il y a le copier-coller) ;
- si je veux améliorer mon texte, il faudra le faire partout où il apparaît (oui, il y a la fonction Remplacer...).
Bref, il serait préférable de se conformer à la philosophie DRY : "Don't Repeat Yourself".
Pour cela, nous allons définir une fonction avec le mot clé def
:
def afficher_texte_site():
print("Vous trouverez plus d'informations en vous rendant sur notre site internet")
Il ne nous reste plus qu'à appeler cette fonction là où c'est utile :
...
afficher_texte_site()
...
...
afficher_texte_site()
...
À vous : définissez puis appelez la fonction afficher_texte_site()
:
À retenir
Définition : une fonction est un bloc d'instructions qui sont exécutées quand cette fonction est appelée.
Pour définir une fonction sans paramètre ni valeur de retour :
def ma_fonction():
instruction_1
instruction_2
instruction_3
...
dernière instruction
Remarquez :
- le
:
indispensable à la fin de la ligne dudef
; - que les instructions sont regroupées dans un bloc qui est délimité grâce à des tabulations (touche Tab ↹) ou par 4 espaces parfois, ce décalage vers la droite est appelé indentation ;
- que ce bloc d’instructions n'est exécuté que si j'appelle ma fonction (en écrivant
ma_fonction()
plus loin dans le programme) ; - que notre fonction
afficher_texte_site
n'a pas de valeur de retour (essayez de tapertype(afficher_texte_site())
), elle ne renvoie rien : elle procède juste à un affichage ; une telle fonction est parfois appelée procédure. L'affichage du texte est un effet de bord.
Exercice
-
Sans utiliser de copier-coller, définissez une fonction nommée
propagande
et qui affiche le texte "Longue vie à notre cher président Tapioca".
Utilisez ensuite cette fonction pour afficher 4 fois ce texte. -
Modifiez la fonction afin de pouvoir afficher "Gloire à notre nouveau président Alcazar !"
Appel d'une fonction⚓︎
Exercice
- Observez et exécutez le script ci-dessous, que remarquez-vous ?
- Ajoutez l'appel aux deux fonctions à la fin du script.
- Placez ces appels au bon endroit.
Explication
Normalement, les instructions sont exécutées du haut vers le bas. Ici, l'introduction de fonctions semblent casser cet ordre mais retenez bien que tant qu'une fonction n'est pas appelée, ses instructions ne sont pas exécutées.
Exercice
- Tapez le script suivant :
🐍 Script Python
aff_texte() def aff_texte(): print("La NSI c'est cool !")
- Exécutez-le puis corrigez l'erreur.
Explication
Vous l'aurez compris, l'erreur ne vient pas du texte mais du fait qu'une fonction ne peut pas être appelée avant d'être définie ! C'est un peu comme si vous parliez chinois à quelqu'un qui ne le parle pas !
Enfin, il est possible d'appeler une fonction dans une autre fonction, testez ceci par exemple :
def aff_texte():
print("La NSI c'est cool !")
def trois_fois_texte():
aff_texte()
aff_texte()
aff_texte()
trois_fois_texte()# appel de la fonction
Fonctions avec paramètre(s) mais sans valeur de retour (procédures)⚓︎
Supposons maintenant que notre texte change un peu :
...
print("Plus d'informations à l'adresse http://site_bidon.com, onglet Actualités")
...
...
print("Plus d'informations à l'adresse http://www.autre_site_bidon.fr, onglet Conseils")
...
Là encore, évitons de nous répéter en définissant une fonction avec ici 2 paramètres :
def afficher_texte_site(adresse, onglet):
print("Plus d'informations à l'adresse", adresse, ", onglet", onglet)
(observez bien la différence entre le mot "onglet" qui n'est que du texte et la variable onglet qui est un des paramètres).
Il ne nous reste plus qu'à appeler cette fonction ainsi :
...
afficher_texte_site("http://site_bidon.com", "Actualités")
...
...
afficher_texte_site("http://www.autre_site_bidon.fr", "Conseils")
...
À vous : redéfinissez puis appelez la fonction afficher_texte_site()
:
À retenir
Pour définir une fonction avec paramètres mais sans valeur de retour :
def ma_fonction(param1, param2, ...):
bloc d’instructions à exécuter dans la fonction
graph LR
%%{init: {'curve': 'basis'}}%%
val([valeur1])--> fonc[["ma_fonction(param1, param2, ...)"]]
val2([valeur2])--> fonc
val3([...])-.-> fonc
classDef params fill:#00BFFF,stroke:#333
classDef foncs fill:#f96,stroke:#333
classDef paramsfac fill:#00BFFF,stroke:#333,stroke-dasharray: 5, 5;
class fonc foncs
class val,val2 params
class val3 paramsfac
Plus d'explications
def ma_fonction(param1, param2, ..., paramN):
instructions
se lit "je définis une fonction nommée ma_fonction
ayant les paramètres param1
, param2
, ..., paramN
qui va exécuter certaines instructions".
Les valeurs des paramètres ne seront connues qu’à l’appel de la fonction. Il s'agit d'écrire quelque chose qui s'adapte aux valeurs des paramètres.
Exercice
Considérons les fonctions suivantes :
def afficher_trois_dieses():
print("###")
def afficher_trois_etoiles():
print("***")
def afficher_trois_plus():
print("+++")
Nous voyons qu'il y a ici une répétition, non pas de texte, mais de principe.
- Écrivez une fonction
afficher_trois
qui aura un paramètre nommésymbole
et qui permettra de remplacer les trois fonctions (et même plus). - Écrivez une fonction
afficher_plusieurs
qui aura deux paramètres nomméssymbole
etnb_fois
et qui permettra d'afficher un nombre quelconque de fois un symbole. - Appelez cette fonction avec
afficher_plusieurs("$", 10)
,afficher_plusieurs("$")
etafficher_plusieurs(10, "$")
.
Commentaire
Attention de bien respecter le nombre (et les types) des paramètres d'une fonction lors de l'appel de celle-ci !
Fonctions sans paramètre mais avec valeur(s) de retour⚓︎
Bien souvent, une fonction renvoie des valeurs. Pour cela, il faut utiliser le mot-clé return
.
Par exemple, si je veux stocker la date de l'armistice de 1945 :
def armistice_2eme_gm():
return "8 mai 1945"
il ne me reste plus ensuite qu'à appeler cette fonction pour récupérer cette date importante :
armistice_2eme_gm()
Remarque
Bien sûr il serait ici plus pertinent de définir une constante plutôt qu'une fonction : ARMISTICE_2EME_GM = "8 mai 1945"
).
Copiez et exécutez les trois lignes de code précédent dans une console (n'oubliez pas l'indentation de 4 espaces pour la deuxième ligne) :
Faîtes maintenant la même chose dans un éditeur :
Explications
Nous avons appelé notre fonction mais rien ne s'affiche !?
En fait cette fonction renvoie bien la valeur "8 mai 1945"... mais à qui ?
- si nous faisons un simple appel comme ci-dessus, la fonction est exécutée, une valeur est renvoyée mais perdue (sauf si nous travaillons dans la console, où elle est affichée);
- nous pouvons utiliser une variable pour récupérer cette valeur :
arm = armistice_2eme_gm()
; - nous pouvons aussi vouloir que la valeur renvoyée soit affichée à l'écran :
print(armistice_2eme_gm())
; - nous pourrions enfin envoyer cette valeur à une autre fonction...
Attention
Ne confondez pas print
et return
. La première fonction ne fait qu'envoyer des valeurs vers l'affichage à l'écran, la seconde renvoie une ou des valeurs, qui pourront être stockées (dans une variable) et utilisées plus tard.
Je ne vois toujours pas la différence !
Par analogie, imaginez que vous jouiez à un jeu sur console et que vous dirigez le joystick vers le haut :
- la manette détecte un mouvement mécanique et renvoie (
return
) à la console un code du genre "dir_haut" ; - une première fonction du programme de la console reçoit le code "dir_haut" et l'envoie (
return
) à d'autres fonctions :- une qui calcule si le personnage peut se déplacer dans cette direction, si oui celle-ci renvoie (
return
) alors les nouvelles coordonnées du personnage ; - une qui récupère ces coordonnées et s'occupe de déplacer/re-dessiner le personnage ;
- une qui s'occupe de gérer les effets du mouvement : changement de score, mort du personnage, etc. et renvoie (
return
) ces informations.
- une qui calcule si le personnage peut se déplacer dans cette direction, si oui celle-ci renvoie (
Remarquez qu'il n'y a pas forcément d'affichage à l'écran de texte donc de print
(heureusement, la console ne va pas vous dire toutes les opérations qu'elle effectue !!).
Au final, la commande return
est bien plus utilisée et importante que print
!
À retenir
Pour définir une fonction sans paramètre mais avec valeur(s) de retour :
def ma_fonction():
instruction_1
instruction_1
...
dernière instruction
return resultat1, resultat2, ...
graph LR
%%{init: {'curve': 'basis'}}%%
fonc[["fonc()"]] -->|return| res([resultat 1])
fonc --> res2([resultat 2])
fonc -.-> res3([...])
classDef foncs fill:#f96,stroke:#333
classDef results fill:#9f6,stroke:#333
classDef resfacs fill:#9f6,stroke:#333,stroke-dasharray: 5, 5
class fonc foncs
class res,res2 results
class res3 resfacs
Nous verrons plus tard que la commande return
interrompt le déroulement d'une fonction et n'est pas forcément à la fin de celle-ci.
Une fonction ne renvoie qu'une valeur. Toujours !
En fait techniquement une fonction ne renvoie qu'une valeur. Par exemple :
def fonction_idiote():
return 5, 2
nous voyons que deux valeurs sont renvoyées : 5 et 2. En vérité, la fonction renvoie le couple (5, 2) donc une valeur.
Essayez, pour le vérifier, d'entrer cela dans l'éditeur :
def fonction_idiote():
return 5, 2
resultat = fonction_idiote()
print(resultat)
Exercice
Essayez d'écrire une fonction moyenne_alea
qui :
- choisit trois nombres entiers compris entre 1 et 100 au hasard ;
- calcule et renvoie leur moyenne.
Appelez ensuite cette fonction en affectant le résultat à une variable appelée moy
.
Enfin affichez la valeur de moy
.
Une réponse possible
import random
def moyenne_alea():
somme = random.randint(1, 100) + random.randint(1, 100) + random.randint(1, 100)
return somme / 3
moy = moyenne_alea()
print(moy)
Fonctions avec paramètre(s) et valeur(s) de retour⚓︎
La plupart du temps, une fonction reçoit des données (les paramètres) et en renvoie d'autres.
En voici un exemple :
def euro_vers_dollar(montant):
return montant*0.9953 # en supposant qu'un euro vaut 0,9953 dollar
print(euro_vers_dollar(5)) # pour convertir 5 €
graph LR
val([montant en euros])--> fonc[[euro_vers_dollar]] --> res([montant en dollars])
classDef params fill:#00BFFF,stroke:#333
classDef foncs fill:#f96,stroke:#333
classDef results fill:#9f6,stroke:#333
class fonc foncs
class val params
class res results
À retenir
Pour définir une fonction dans le cas général :
def
ma_fonction
(
param1, param2, ...
):
instruction_1
instruction_2
...
instruction_n
return
resultat1, resultat2, ...
graph LR
%%{init: {'curve': 'basis'}}%%
val([valeur1])--> fonc[["fonc1(param1, param2)"]] -->|return| res([resultat 1])
val2([valeur2])--> fonc
val3([...])-.-> fonc
fonc --> res2([resultat 2])
fonc -.-> res3([...])
classDef params fill:#00BFFF,stroke:#333
classDef foncs fill:#f96,stroke:#333
classDef paramsfac fill:#00BFFF,stroke:#333,stroke-dasharray: 5, 5
classDef results fill:#9f6,stroke:#333
classDef resfacs fill:#9f6,stroke:#333,stroke-dasharray: 5, 5
class fonc,fonc2 foncs
class val,val2 params
class val3 paramsfac
class res,res2 results
class res3 resfacs
Ne rien renvoyer ou renvoyer rien
- Nous l'avons vu, certaines fonctions, dites procédures, ne renvoient rien (elles n'ont pas de
return
). Il est donc inutile de taper dans ce cas quelque chose du genrea = ma_fonction(...)
. - Dans ce cas, la fonction renvoie la valeur rien (
None
en Python). Par exemple, une fonction qui prend trois nombres entiers et renvoie ceux qui sont pairs devra parfois renvoyer... rien (return None
). Ce rien constitue alors une information (ici : il y a pas de nombres pairs parmi les trois nombres).
Un bouton en plus
Les éditeurs des exercices suivants comportent le bouton en plus. Quand vous aurez modifié le code, cliquez sur ce bouton pour le faire vérifier.
Exercice
Écrivez puis testez (avec un print
) une fonction qui convertit des degrés Celsius en degrés Fahrenheit (je vous laisse chercher la formule sur le net).
Exercice
Écrivez et testez une fonction qui prend en paramètres deux nombres et renvoie leur produit et leur somme.
Exemple d'appel :
>>> produit_et_somme(5,3)
(15, 8)
Exercice
Écrivez et testez une fonction qui prend deux chaînes de caractères, par exemple "Marc" et "Julie" et renvoie dans ce cas la chaîne suivante : Marc + Julie = 💙
.
Exercice
Fonctions utilisées dans des fonctions⚓︎
Dans certains cas, les valeurs renvoyées par une fonction peuvent être utilisées par (deviennent les paramètres d') une autre fonction.
graph LR
%%{init: {'curve': 'basis'}}%%
val([val1])--> fonc[["fonc1(par1, par2)"]] -->|return| res[[res1]] --> fonc2[["fonc2(par1, par2, par3)"]] -->|return| rf[[res]]
val2([val2])--> fonc
fonc --> res2[[res2]] --> fonc2
fonc --> res3[[res3]] --> fonc2
classDef params fill:#00BFFF,stroke:#333
classDef foncs fill:#f96,stroke:#333
classDef paramsfac fill:#00BFFF,stroke:#333,stroke-dasharray: 5, 5
classDef results fill:#9f6,stroke:#333
classDef rfs fill:#9f6,stroke:#333
class fonc,fonc2 foncs
class val,val2 params
class res,res2,res3 results
class rf rfs
En voici un exemple :
def europy-undverspy-unddollar(montant):bksl-nl return montant py-str 0.99 # en supposant qu'un euro vaut 0,99 dollarbksl-nlbksl-nldef dollarpy-undverspy-undyuan(montant):bksl-nl return montant py-str 6.81 # en supposant qu'un dollar vaut 6,81 yuansbksl-nlbksl-nldef europy-undverspy-undyuan(montant):bksl-nl montantpy-unddollar = europy-undverspy-unddollar(montant)bksl-nl montantpy-undyuan = dollarpy-undverspy-undyuan(montantpy-unddollar)bksl-nl return montantpy-undyuanbksl-nlbksl-nl# remarque : rien ne va s'afficher lors de l'exécution de ce scriptbksl-nl# vous pouvez appeler la fonction europy-undverspy-undyuan dans la consolebksl-nl
A
Z
La fonction euro_vers_yuan
appelle les deux fonctions euro_vers_dollar
et dollar_vers_yuan
.
Nous pourrions écrire cette fonction de façon plus concise (mais moins claire ?) :
def euro_vers_yuan(montant):
return dollar_vers_yuan(euro_vers_dollar(montant))
graph LR
val([euros])--> fonc[[euro_vers_dollar]] -->|return| res([dollars])--> fonc2[["dollar_vers_yuan(montant)"]] -->|return| rf([yuans])
val--> fonc3[[euro_vers_yuan]] -->|return| rf
classDef params fill:#00BFFF,stroke:#333
classDef foncs fill:#f96,stroke:#333
classDef paramsfac fill:#00BFFF,stroke:#333,stroke-dasharray: 5, 5
classDef results fill:#9f6,stroke:#333
classDef rfs fill:#9f6,stroke:#333
class fonc,fonc2,fonc3 foncs
class val params
class res results
class rf rfs
Exercice
- Écrivez une fonction
demande_prenom
qui affiche "Quel est votre prénom ?", demande une réponse et renvoie cette réponse. - Écrivez une fonction
demande_naissance
qui affiche "En quelle année êtes-vous né ?", demande une réponse et renvoie cette réponse. - Écrivez une fonction
demande_infos
qui utilise les deux précédentes et affiche "Bonjour le prénom entré, vous avez environ l'âge approximatif de la personne".
Exercice
- Écrivez une fonction
de_4
qui simule un dé tétraédrique en renvoyant un nombre entier entre 1 et 4. - Écrivez une fonction
lancer_3_des
qui utilise la précédente et renvoie la somme et la moyenne de trois lancers d'un dé tétraédrique.
Petit bêtisier des erreurs courantes sur les fonctions
- N'utilisez pas des paramètres constants.
Les paramètres d'une fonction sont toujours des variables.
Par exemple, la fonction suivante :🐍 Script Pythonn'a aucune capacité d'adaptation (ses paramètres sont toujoursdef mafonction(4, "Toto"): print(4 * "Toto")
4
et"Toto"
, on pourrait tout aussi bien les retirer) et de toute façon Python n'acceptera pas cette syntaxe. - N'écrasez pas les paramètres.
Par exemple, dans la fonction suivante :🐍 Script Pythonil n'y a pas d'erreur de syntaxe mais à quoi sert le paramètredef mafonction(a, b): a = 3 print(a * b)
a
si on écrase immédiatement sa valeur ? - La valeur renvoyée par la fonction doit être récupérée ou affichée :
🐍 Script Pythonfait bien ce que l'on attend (calculer le produit de 4 et de 5) mais la valeur renvoyée (le produit) est perdu ! Il faut donc suivant les besoins soit affecter le résultat à une variable :def mafonction(a, b): produit = a * b return produit mafonction(4, 5)
🐍 Script Pythonsoit l'afficher :def mafonction(a, b): produit = a * b return produit resultat = mafonction(4, 5)# stockage pour utilisation ultérieure
🐍 Script Pythonsoit l'utiliser dans une autre fonction :def mafonction(a, b): produit = a * b return produit print(mafonction(4, 5))# affichage immédiat
🐍 Script Pythondef mafonction_idiote(a): print(a) def mafonction(a, b): produit = a * b return produit mafonction_idiote(mafonction(4, 5))# la fonction idiote va ici afficher 20
- N'utilisez pas le nom de la fonction en tant que variable :
🐍 Script Pythonfonctionne mais la lisibilité de l'ensemble est lamentable (et ça bugguerait peut-être dans d'autres langages moins permissifs).def mafonction(a, b): mafonction = a * b return mafonction
- De préférence, utilisez des noms de fonctions et de variables "parlants" :
🐍 Script Pythonest mieux que :
def multiplier_texte(nb, texte): return nb * texte
🐍 Script PythonPensez à la personne (vous par exemple) qui va relire votre code dans 6 mois...def f(a, b): return a * b