Aller au contenu

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 :

🐍 Script Python
...
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 :

🐍 Script Python
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 :

🐍 Script Python
...
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 :

🐍 Script Python
def ma_fonction():
    instruction_1
    instruction_2
    instruction_3
    ...
    dernière instruction

Remarquez :

  • le : indispensable à la fin de la ligne du def ;
  • 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 taper type(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

  1. Observez et exécutez le script ci-dessous, que remarquez-vous ?
  2. Ajoutez l'appel aux deux fonctions à la fin du script.
  3. Placez ces appels au bon endroit.

###
print("Les Tortues (Testudines), ou Chéloniens,")bksl-nlbksl-nldef affpy-undtexte():bksl-nl print(" forment un ordre de reptiles dont la caractéristique est")bksl-nlbksl-nlprint(" d'avoir une carapace. ")bksl-nlprint("Il existe actuellement (décembre 2019) 343")bksl-nlbksl-nldef affpy-undautrepy-undtexte():bksl-nl print(" espèces recensées possédant des caractéristiques diverses.")bksl-nlbksl-nlprint("Source : Wikipédia.")bksl-nlbksl-nl

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

  1. Tapez le script suivant :
    🐍 Script Python
    aff_texte()
    
    def aff_texte():
        print("La NSI c'est cool !")
    
  2. 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 :

🐍 Script Python
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 :

🐍 Script Python
...
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 :

🐍 Script Python
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 :

🐍 Script Python
...
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 :

🐍 Script Python
def ma_fonction(param1, param2, ...):
    bloc dinstructions à 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
🐍 Script Python
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 :

🐍 Script Python
    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és symbole et nb_fois et qui permettra d'afficher un nombre quelconque de fois un symbole.
  • Appelez cette fonction avec afficher_plusieurs("$", 10), afficher_plusieurs("$") et afficher_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 :

🐍 Script Python
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 :

🐍 Script Python
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.

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 :

🐍 Script Python
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 :

🐍 Script Python
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 :

🐍 Script Python
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
🐍 Script Python
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 :

🐍 Script Python
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 genre a = 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).

###
benchmark = ['celsiuspy-undverspy-undFahrenheit(0)==32',bksl-nl 'celsiuspy-undverspy-undFahrenheit(10)==50',bksl-nl 'celsiuspy-undverspy-undFahrenheit(100)==212']bksl-nlbksl-nl 5/5
# création d'une fonctionbksl-nldef celsiuspy-undverspy-undFahrenheit(degres):bksl-nl pass # instruction à remplacer par votre codebksl-nlbksl-nl# appel de cette fonction : conversion de 100 degrés Celsiusbksl-nlprint(celsiuspy-undverspy-undFahrenheit(100))bksl-nldef celsiuspy-undverspy-undFahrenheit(degres):bksl-nl return degres py-str 1.8 + 32bksl-nl

Exercice

Écrivez et testez une fonction qui prend en paramètres deux nombres et renvoie leur produit et leur somme.

Exemple d'appel :

🐍 Script Python
>>> produit_et_somme(5,3)
(15, 8)

###
benchmark = ['produitpy-undetpy-undsomme(1,2) == (2,3)',bksl-nl 'produitpy-undetpy-undsomme(4,3) == (12,7)',bksl-nl 'produitpy-undetpy-undsomme(5,0) == (0,5)']bksl-nl 5/5
def produitpy-undetpy-undsomme(a, b):bksl-nl pass # instruction à remplacer par votre codebksl-nldef produitpy-undetpy-undsomme(a, b):bksl-nl return a py-str b, a + bbksl-nl

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 = 💙.

###
benchmark = ['love("Roméo", "Juliette")"Roméo + Juliette = 💙"',bksl-nl 'love("Marc","Julie")"Marc + Julie = 💙"']bksl-nl 5/5
def love():bksl-nl pass # instruction à remplacer par votre codebksl-nldef love(personne1, personne2):bksl-nl return personne1 + " + " + personne2 + " = 💙"bksl-nlbksl-nl

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 ?) :

🐍 Script Python
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

  1. Écrivez une fonction demande_prenom qui affiche "Quel est votre prénom ?", demande une réponse et renvoie cette réponse.
  2. Écrivez une fonction demande_naissance qui affiche "En quelle année êtes-vous né ?", demande une réponse et renvoie cette réponse.
  3. É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

dé 4 faces

  1. Écrivez une fonction de_4 qui simule un dé tétraédrique en renvoyant un nombre entier entre 1 et 4.
  2. É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

  1. N'utilisez pas des paramètres constants.
    Les paramètres d'une fonction sont toujours des variables.
    Par exemple, la fonction suivante :
    🐍 Script Python
    def mafonction(4, "Toto"):
        print(4 * "Toto")
    
    n'a aucune capacité d'adaptation (ses paramètres sont toujours 4 et "Toto", on pourrait tout aussi bien les retirer) et de toute façon Python n'acceptera pas cette syntaxe.
  2. N'écrasez pas les paramètres.
    Par exemple, dans la fonction suivante :
    🐍 Script Python
    def mafonction(a, b):
        a = 3
        print(a * b)
    
    il n'y a pas d'erreur de syntaxe mais à quoi sert le paramètre a si on écrase immédiatement sa valeur ?
  3. La valeur renvoyée par la fonction doit être récupérée ou affichée :
    🐍 Script Python
    def mafonction(a, b):
        produit = a * b
        return produit
    mafonction(4, 5)
    
    fait 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 :
    🐍 Script Python
    def mafonction(a, b):
        produit = a * b
        return produit
    resultat = mafonction(4, 5)# stockage pour utilisation ultérieure
    
    soit l'afficher :
    🐍 Script Python
    def mafonction(a, b):
        produit = a * b
        return produit
    print(mafonction(4, 5))# affichage immédiat
    
    soit l'utiliser dans une autre fonction :
    🐍 Script Python
    def 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
    
  4. N'utilisez pas le nom de la fonction en tant que variable :
    🐍 Script Python
    def mafonction(a, b):
        mafonction = a * b
        return mafonction
    
    fonctionne mais la lisibilité de l'ensemble est lamentable (et ça bugguerait peut-être dans d'autres langages moins permissifs).
  5. De préférence, utilisez des noms de fonctions et de variables "parlants" :
    🐍 Script Python
    def multiplier_texte(nb, texte):
        return nb * texte
    
    est mieux que :
    🐍 Script Python
    def f(a, b):
        return a * b
    
    Pensez à la personne (vous par exemple) qui va relire votre code dans 6 mois...