Une fonction en Python : nommer un bloc de code réutilisable

Une fonction regroupe sous un nom un bloc de code que l'on peut rejouer autant de fois qu'on veut : elle reçoit des arguments et renvoie un résultat.
6 min de lecture
Believemy logo

Définition

Écrivez un programme un peu long, et le même bloc de code finit par revenir : le calcul d'une remise, la mise en forme d'une date. Vous le recopiez, puis vous le recopiez encore. Le jour où la règle change, il faut retrouver toutes les copies, et il y en a toujours une qui échappe. C'est cet ennui qu'une fonction fait disparaître.

Une fonction regroupe sous un seul nom un bloc de code que l'on veut pouvoir rejouer. Elle reçoit éventuellement des données en entrée, ses arguments, et renvoie un résultat avec return. On l'appelle en tapant son nom suivi de parenthèses, et la règle qu'elle contient n'existe plus qu'à un seul endroit.

En Python, elle se déclare avec def. Voici la plus petite qui soit vraiment utile : un nom, un argument obligatoire, un argument optionnel, un résultat renvoyé.

PYTHON
def remise(prix, taux=0.1):
    """Applique une remise et arrondit au centime."""
    return round(prix * (1 - taux), 2)

remise(100)         # 90.0, le taux par défaut s'applique
remise(100, 0.25)   # 75.0, on fournit son propre taux

Le taux=0.1 de la déclaration est une valeur par défaut : il rend cet argument facultatif, ce qui explique que le premier appel fonctionne avec un seul nombre.

La chaîne placée juste en dessous est la docstring. Elle documente la fonction là où on la lit, et les éditeurs la récupèrent pour vous l'afficher au moment de l'appel.


Ce que vous y gagnez

Le premier gain est celui qu'on vient de décrire : la répétition s'arrête. Un bug corrigé dans une fonction est corrigé partout où elle est appelée, y compris aux endroits que vous aviez oubliés.

Le deuxième est moins visible et plus précieux sur la durée : une fonction donne un nom à une intention. La ligne remise(prix) se lit sans effort, là où trois lignes de multiplication et d'arrondi obligent à reconstituer ce qu'elles calculent. Et un nom, contrairement à un commentaire, ne se périme pas en silence.

Le troisième est l'isolement. Ce qui se passe à l'intérieur ne dépend que des arguments reçus, jamais du reste du programme. Vous pouvez donc appeler la fonction avec des valeurs choisies et vérifier son résultat sans lancer l'application : c'est exactement ce que fait un test automatisé.

D'où une règle simple : une fonction fait une seule chose. Si son nom contient un « et », elle en fait probablement deux, et gagnerait à être coupée.


Ce qui entre : les arguments

Les arguments se fournissent dans l'ordre de la déclaration, ou par leur nom. La seconde forme allonge l'appel mais le rend bien plus clair dès qu'il y a plusieurs valeurs : remise(100, taux=0.25) se comprend sans rouvrir la fonction.

Une valeur par défaut rend un argument optionnel. C'est la façon habituelle de faire évoluer une fonction sans casser l'existant : les appels déjà écrits, qui ignorent le nouvel argument, continuent de fonctionner.

Reste le cas où vous ignorez combien d'arguments vous allez recevoir. Deux formes le couvrent : args pour un nombre libre d'arguments ordinaires, kwargs pour un nombre libre d'arguments nommés.

Bon à savoir

Une valeur par défaut est évaluée une seule fois, à la déclaration, et non à chaque appel. Avec un objet modifiable comme liste=[], c'est la même liste qui resservira d'un appel au suivant, et elle se remplira sans que rien ne le signale. L'usage est d'écrire liste=None et de créer la liste dans le corps de la fonction.


Ce qui sort : le retour

return met fin à l'appel et renvoie une valeur là où la fonction a été appelée. Cette valeur redevient une donnée ordinaire : vous pouvez la ranger dans une variable, la passer à une autre fonction, ou l'afficher.

Une fonction sans return renvoie None. Ce n'est pas un défaut : c'est le cas normal d'une fonction qui agit au lieu de calculer, comme enregistrer un fichier. On ne lui demande pas un résultat, on lui demande un effet.

Attention

Confondre afficher et renvoyer est l'erreur la plus fréquente des débuts. Une fonction qui se termine par print(resultat) au lieu de return resultat affiche bien la bonne valeur, mais renvoie None. Le programme ne casse que plus loin, quand on réutilise ce résultat, et on cherche la panne au mauvais endroit.


Ce qui reste à l'intérieur

Une variable créée dans une fonction n'existe que le temps de l'appel, puis disparaît. Elle est invisible depuis l'extérieur, et c'est une bonne nouvelle : vous pouvez utiliser un compteur i dans dix fonctions sans qu'elles se marchent dessus. Cette zone de vie d'un nom s'appelle la portée.

L'inverse n'est pas vrai. Une fonction peut lire une variable définie au niveau du module, mais elle ne peut pas la modifier sans le déclarer avec global. Python refuse de le faire dans votre dos.

Prenez-le comme un signal : une fonction qui modifie une variable extérieure perd l'isolement qui faisait sa valeur. Recevoir la valeur en argument et renvoyer la nouvelle version donne presque toujours un code plus simple à relire.


Questions fréquentes

Question

Quelle différence entre une fonction et une méthode ?

Une méthode est une fonction définie dans une classe et rattachée à un objet, qu'elle reçoit en premier argument sous le nom self. Tout ce que vous venez de lire reste vrai pour elle : arguments, valeurs par défaut, retour, portée. La différence tient à ce qu'elle a sous la main.

Question

Combien d'arguments une fonction devrait-elle avoir ?

Le moins possible. Au-delà de quatre ou cinq, l'appel devient difficile à lire et facile à se tromper d'ordre. Une longue liste signale souvent que les arguments forment une même notion, une adresse ou une commande : un dictionnaire ou une dataclass l'exprime mieux.

Question

Une fonction peut-elle en appeler une autre ?

Oui, sans limite, et elle peut même s'appeler elle-même : c'est la récursivité. Il faut alors prévoir un cas d'arrêt, sans quoi Python interrompt le programme avec une RecursionError au bout d'un millier d'appels imbriqués. Découper un programme en fonctions courtes est la compétence qui sépare un script qui marche d'un code qu'on peut reprendre six mois plus tard, et notre formation Python y consacre une partie entière.

Termes connexes

Découvrez notre glossaire Python

Parcourez les termes et définitions les plus couramment utilisés dans le domaine du développement avec Python.

Partager cet article

Tu veux nous aider ? Fais un lien vers cet article sur tes réseaux ou encore mieux : sur ton site, dans un article ou dans ta newsletter.