Les fonctions en Python
Le mot-clé def
est un pilier de la programmation en Python.
Il permet de définir des fonctions nommées, c’est-à-dire des blocs de code que l’on peut réutiliser plusieurs fois dans un programme.
Utiliser des fonctions permet de :
- Structurer son code de manière modulaire ;
- Réutiliser des instructions sans les répéter ;
- Améliorer sa lisibilité tout en augmentant sa maintenabilité.
Le mot-clé def
est donc la porte d’entrée vers une programmation plus propre, plus efficace et plus évolutive.
Syntaxe de base avec def
La syntaxe de base d’une fonction en Python est très simple :
def nom_de_la_fonction(parametres):
instruction(s)
return résultat
Voici les éléments clés d'une fonction avec Python :
def
est le mot-clé utilisé pour définir une fonction ;- On suit avec le nom de la fonction, puis une liste de paramètres entre parenthèses ;
- Le bloc d’instructions est indenté (souvent 4 espaces).
Enfin, on peut retrouver le mot-clé return
qui permet de renvoyer une valeur au moment de l’appel de la fonction.
En Python, l’indentation est obligatoire. C’est ce qui délimite le bloc d’instructions appartenant à la fonction. 😉
Exemple simple de fonction avec def
Prenons un exemple de fonction très simple :
def addition(x, y):
return x + y
resultat = addition(3, 5)
print(resultat) # Résultat : 8
Dans cet exemple :
def addition(x, y)
crée une fonction appeléeaddition
qui prend deux paramètres ;- Elle retourne la somme des deux (
x + y
) ; - On appelle notre fonction en utilisant son nom suivi des arguments que l'on souhaite lui donner avec
addition(3, 5)
qui renvoie 8, que l’on stocke dans une variableresultat
; - Enfin,
print()
affiche ce résultat.
Une fonction peut être appelée plusieurs fois avec des arguments différents. C’est ce qui en fait un outil puissant pour automatiser des opérations répétitives. 😋
Paramètres et arguments
Comme nous l'avons vu rapidement dans notre exemple précédent, les fonctions définies avec def
peuvent recevoir des paramètres, c’est-à-dire des valeurs d’entrée qui influencent leur comportement.
Les types de paramètres en Python
Les paramètres positionnels
Ils sont passés dans l’ordre défini :
def saluer(prenom, nom):
return f"Bonjour {prenom} {nom}"
print(saluer("Alice", "Durand")) # Bonjour Alice Durant
Comme on peut le voir, la première valeur donnée "Alice" sera automatiquement attribuée sur la variable prenom
de notre fonction. Idem pour "Durand" qui ira dans l'argument en seconde position.
Ceci veut dire que si l'on inverse "Alice" et "Durant", le prénom et le nom de notre utilisateur sera ... inversé ! 😬
def saluer(prenom, nom):
return f"Bonjour {prenom} {nom}"
print(saluer("Durant", "Alice")) # Bonjour Durant Alice
Ici "Durant" va bien dans la variable prenom
, alors que "Alice" va dans la variable nom
. C'est ce qu'on appelle des paramètres positionnels.
Les arguments nommés
Au contraire des paramètres positionnels, ils permettent de spécifier la valeur d’un paramètre, quel que soit l’ordre :
def saluer(prenom, nom):
return f"Bonjour {prenom} {nom}"
print(saluer(nom="Durant", prénom="Alice"))
# Bonjour Alice Durant
Ici, nous n'aurons plus aucun problème d'inversement !
Les valeurs par défaut
Enfin on peut attribuer une valeur par défaut à un paramètre :
def saluer(prenom, nom="Dupont"):
return f"Bonjour {prenom} {nom}"
print(saluer("Julie")) # Bonjour Julie Dupont
Les paramètres avec valeurs par défaut doivent toujours être placés après les paramètres obligatoires.
Retourner une valeur avec return
Le mot-clé return
permet à une fonction de renvoyer une valeur. C’est ce qui différencie une fonction qui "renvoie" d’une fonction qui "fait" quelque chose.
On l'utilise généralement pour :
- Obtenir un résultat calculé par la fonction ;
- Chaîner plusieurs fonctions entre-elles ;
- Séparer traitement et affichage.
Prenons un petit exemple :
def carre(x):
return x ** 2
résultat = carre(4)
print(résultat) # 16
Une fonction peut contenir plusieurs return, ce qui permet de retourner différents résultats selon les conditions.
PYTHONdef evaluation(note): if note >= 10: return "Réussi" else: return "Échoué"
Les fonctions qui ne retournent jamais rien
Contrairement à ce que nous venons de voir, toutes les fonctions ne retournent pas une valeur explicite.
Parfois, une fonction modifie un objet existant ou effectue une action externe (affichage, écriture fichier, etc.).
Prenons un autre exemple pour illustrer tout ça :
def dire_bonjour(nom):
print(f"Bonjour {nom}")
Cette fonction affiche directement une chaîne de caractères, sans jamais retourner quelque chose !
Les annotations de type sur les fonctions
Python permet d’ajouter des annotations de types aux paramètres et au retour d’une fonction. Cela ne change pas le comportement de la fonction, mais facilite la lecture du code et permet aux éditeurs ou outils de linters d’analyser les types attendus.
Voici un exemple :
def saluer(prenom: str, age: int) -> str:
return f"Bonjour {prenom}, vous avez {age} ans."
Les annotations de type indiquent :
- que
prenom
est une chaîne de caractères ; - qu'
age
est un entier ; - que cette fonction retourne une chaîne.
Attention, ces annotations ne sont pas obligatoires et ne sont pas exécutées. Python ne vérifie pas les types à l’exécution. Elles ressemblent beaucoup à ce qu'on peut trouver avec TypeScript.
Le typage permet d'améliorer la documentation du code, de mieux travailler en équipe tout en évitant des erreurs avec des outils comme MyPy.
Docstrings et documentation
Il est courant de voir des docstrings quand on parle des fonctions ! 😉
Une docstring (pour documentation string 🇺🇸) est un commentaire placé juste après la définition d’une fonction, destiné à décrire ce que fait cette fonction.
Syntaxe d'une docstring
def multiplier(a: int, b: int) -> int:
"""Multiplie deux nombres entiers et retourne le résultat."""
return a * b
Comme on peut le voir dans notre exemple, une docstring doit être :
- délimitée par trois guillemets doubles
"""
ou trois guillemets simples'''
; - rédigée de manière claire et concise ;
- respecter les conventions de la PEP 257.
Exemple complet d'une docstring sur une fonction
def calcul_aire_rectangle(longueur: float, largeur: float) -> float:
"""
Calcule l'aire d'un rectangle.
Paramètres :
- longueur : float, la longueur du rectangle
- largeur : float, la largeur du rectangle
Retour :
- float : l'aire calculée
"""
return longueur * largeur
Pas mal non ? 😊
Les docstrings sont lues automatiquement par
help()
et les IDE (les éditeurs de code). Elles améliorent grandement la compréhension du code, surtout en travail collaboratif.
La portée des variables dans une fonction (scope)
La portée d’une variable désigne l’endroit du programme où cette variable est accessible.
En Python, les variables définies dans une fonction ne sont pas accessibles en dehors.
Les variables locales
Il s'agit d'une variable créée à l’intérieur d’une fonction.
def dire_bonjour():
message = "Bonjour"
print(message)
print(message) # Erreur car message n'existe que pour dire_bonjour()
Les variables globales
Une variable définie à l’extérieur de toute fonction est globale, donc visible dans tout le programme... mais pas modifiable depuis une fonction sans utiliser le mots-clé global
.
nom = "Alice"
def changer_nom():
global nom
nom = "Bob"
changer_nom()
print(nom) # Bob
L’usage de
global
est souvent déconseillé car cela rend le code plus difficile à suivre. Il est préférable de retourner une valeur et de la stocker.
Les fonctions imbriquées (nested functions)
En Python, il est possible de définir une fonction à l’intérieur d’une autre fonction. C'est justement ce qu'on appelle une fonction imbriquée. 👀
Prenons un exemple :
def exterieur():
def interieur():
return "Bonjour depuis l’intérieur !"
return interieur()
Dans notre exemple, il faut appeler exterieur()
qui va appeler interieur()
pour retourner notre chaîne de caractères.
Ok c'est super, mais à quoi ça sert ?
À beaucoup de choses ! 🥸
On peut par exemple :
- encapsuler une logique qui ne sera utilisée qu'à un endroit précis ;
- créer des fonctions fermées (closures) ;
- améliorer la lisibilité dans certains cas complexes.
Les fonctions récursives
Comme nous l'avons vu, les fonctions peuvent s'imbriquer.
Grâce à cette possibilité, nous pouvons créer ce qu'on appelle des fonctions récursives.
Une fonction récursive est une fonction qui s'appelle elle-même. C’est utile pour résoudre des problèmes de manière décomposée, comme les calculs de factorielle, de suites, ou de parcours d’arborescences.
Voici un exemple très classique :
def factorielle(n):
if n == 0:
return 1
else:
return n * factorielle(n - 1)
print(factorielle(5)) # 120
Une récursion mal contrôlée peut provoquer un dépassement de pile (
RecursionError
). Assurez-vous d’avoir un cas de sortie. Autrement, vous allez créer une boucle infinie (ce qui fera planter votre code) !
Les fonctions sont des objets
En Python, les fonctions sont considérées comme des objets.
Ceci veut dire qu'il est possible de les assigner à des variables, de les passer en paramètre ou encore de les stocker dans des listes par exemple.
Voici chacun des cas dans des petits exemples !
Assigner une fonction à une variable
def bonjour():
return "Salut !"
dire = bonjour
print(dire()) # Salut !
ici, on assigne la fonction bonjour
à notre variable dire
. Autrement dit, on peut désormais executer dire
comme une fonction !
Passer une fonction comme argument
def appliquer(fonction, valeur):
return fonction(valeur)
def doubler(x):
return x * 2
resultat = appliquer(doubler, 5)
print(resultat) # 10
Dans cet exemple, on passe la fonction doubler
dans notre fonction appliquer
: c'est très utilisé pour décomposer nos fonctions en plusieurs petites fonctions.
Stocker dans une liste
def a(): return "A"
def b(): return "B"
fonctions = [a, b]
for f in fonctions:
print(f()) # A puis B
Dans cet exemple, on assigne les fonctions a
et b
dans une liste pour pouvoir les réutiliser plus tard (dans une boucle ici).
Les fonctions génératrices avec yield
Il est également possible d'utiliser ce qu'on appelle des générateurs.
Ce sont des fonctions spéciales qui utilisent le mot-clé yield
au lieu de return
.
Ils retournent toujours un élément à la fois, ce qui permet de gérer de grands volumes de données sans tout charger en mémoire. Petit bonus : ils conversevent l'état d'exécution entre chaque appel !
Par exemple :
def compte():
for i in range(3):
yield i
for nombre in compte():
print(nombre) # 0, 1, 2
yield
suspend l’exécution de la fonction et la reprend au même endroit lors du prochain appel.
Les décorateurs sur les fonctions
Les décorateurs sont des fonctions qui modifient ou enrichissent une autre fonction sans la modifier directement. Ils sont très utilisés dans les frameworks comme Flask ou Django.
Ils prennent une fonction en argument et retournent une fonction modifiée.
Ils sont très utilisés dans des contextes comme la journalisation, la validation d'entrée ou encore l'authentification.
Prenons un exemple très simple : imaginez que vous souhaitiez afficher "== Début ==" et "== Fin ==" à chaque appel d’une fonction sans changer son code. Plutôt que de modifier toutes vos fonctions, vous utilisez un décorateur.
def afficher_balises(fonction):
def wrapper():
print("== Début ==")
fonction()
print("== Fin ==")
return wrapper
@afficher_balises
def dire_bonjour():
print("Bonjour à tous !")
dire_bonjour()
Ce qu'il se passe en boulisses :
def afficher_balises(fonction)
: une fonction qui prend une autre fonction en paramètre ;- à l’intérieur, nous définissons
wrapper()
qui appelle la fonction originale tout en ajoutant du code avant et après ; - en appliquant
@afficher_balises
avant une autre fonction, Python exécute notre décorateur en passant en argument la fonctiondire_bonjour()
définie juste après !
Les décorateurs rendent donc notre code plus modulaire et réutilisable, tout en gardant vos fonctions propres.
Fonctions anonymes (lambda) vs nommées
En Python, il existe deux façons principales de définir des fonctions :
- avec des fonctions anonymes appelées aussi fonctions lambda ;
- ou avec des fonctions nommées qu'on appelle aussi des fonctions traditionnelles (ce qu'on voit justement ici).
Tableau comparatif
Type de fonction | Syntaxe | Cas d'usage principal |
Nommée (def ) | def nom(): | Factorisation et documentation |
Anonyme (lambda ) | lambda args: expr | Opérations ponctuelles et uniques |
Exemples comparés
# Fonction nommée
def ajouter(x, y):
return x + y
# Fonction lambda équivalente
lambda_ajouter = lambda x, y: x + y
Préférez
def
quand la fonction est complexe, longue ou utilisée plusieurs fois.
Bonnes pratiques d'écriture avec def
Plusieurs bonnes pratiques sont nécessaires pour faire des fonctions qui respectent les conventions en Python.
Pour commencer, il faut utiliser des noms clairs et éviter les noms trop vagues :
calculer_aire_triangle(base, hauteur)
est très représentatif ;c(x, y)
est beaucoup trop abstrait.
Il convient aussi de respecter la norme PEP 8, autrement dit :
- il faut mettre deux lignes vides entre les définitions de fonctions (sauf dans les classes) ;
- et il faut nommer les fonctions avec des mots en minuscules séparés par des underscores.
Erreurs courantes avec def
Faisons un petit tour des erreurs les plus courantes quand on crée des fonctions avec def
en Python.
Oublier le return
def calcul(x, y):
x + y # Rien n’est retourné !
print(calcul(2, 3)) # Affiche : None
Il faut bien penser à ajouter le mot-clé return
:
def calcul(x, y):
return x + y
Avoir une mauvaise indentation
def test():
print("Bonjour") # Erreur d’indentation !
Une mauvaise indentation empêche Python de comprendre quand s'arrête une fonction.
def test():
print("Bonjour")
Oublier les parenthèses pour appeler une fonction
def bonjour():
return "Salut"
print(bonjour) # Erreur
C'est une erreur classique ! 👀
def bonjour():
return "Salut"
print(bonjour()) # Les parenthèses sont ajoutées
Questions fréquentes sur def
Peut-on avoir plusieurs
return
?
Oui. Cela permet de renvoyer différentes valeurs selon les conditions.
Doit-on typer toutes les fonctions ?
Non, comme nous l'avons vu ensemble les annotations de type sont facultatives.
Qu'est-ce qu'une fonction récursive ?
Une fonction qui s'appelle elle-même. Elle permet de traiter des problèmes de manière décomposée.
Comment se former à Python ?
En rejoignant notre formation Python !