Fonctions et modularité
Cours · Python (école d'ingénieur), chapitre 4 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
4.1 Introduction et Motivation
Pour écrire des programmes clairs, déboguables et réutilisables, il est nécessaire de découper le code en blocs logiques indépendants appelés fonctions. Une fonction prend des paramètres d'entrée, effectue un traitement spécifique et retourne un ou plusieurs résultats. En encapsulant le code de cette manière, on évite les duplications (principe DRY - Don't Repeat Yourself) et on facilite les tests unitaires. Ce chapitre détaille la définition des fonctions, le passage d'arguments (positionnels, par mot-clé, valeurs par défaut), la règle de portée des variables, et les subtilités liées à la mutabilité des objets en Python.
4.2 Définition et Appel de Fonctions
En Python, une fonction est définie avec le mot-clé def.
Une fonction comporte un nom, une liste d'arguments entre parenthèses, et un bloc d'instructions indenté. La valeur finale renvoyée à l'appelant est spécifiée par le mot-clé return. Si aucune instruction return n'est présente, la fonction renvoie implicitement l'objet spécial None.
def nom_fonction(arg1, arg2):
"""Docstring : décrit brièvement le rôle de la fonction."""
# Traitement
return resultat
def factorielle(n):
"""Calcule la factorielle d'un entier n (n! = n x (n-1) x ... x 1)."""
res = 1
for i in range(2, n + 1):
res *= i
return res
val = factorielle(5)
print(val) # Affiche 120
4.2.1 Docstrings et Bonnes Pratiques
Une docstring (chaîne de documentation) est une chaîne de caractères placée immédiatement après l'en-tête d'une fonction. Elle décrit son rôle, ses paramètres et sa valeur de retour. Elle est accessible via help(nom_fonction) ou l'attribut nom_fonction.__doc__.
La docstring doit être rédigée entre triples guillemets (""" ... """) et peut s'étendre sur plusieurs lignes. C'est une bonne pratique systématique pour toute fonction non triviale.
4.3 Arguments et Valeurs par Défaut
Python offre une grande flexibilité dans le passage d'arguments.
Il est possible de définir des valeurs par défaut pour certains arguments. Lors de l'appel de la fonction, ces arguments deviennent optionnels. On peut également appeler une fonction en nommant explicitement ses paramètres :
def saluer(nom, message="Bonjour"):
return f"{message}, {nom}!"
saluer("Alice")renvoie"Bonjour, Alice!"saluer("Bob", "Bienvenue")renvoie"Bienvenue, Bob!"saluer(message="Salut", nom="Eve")(appel par mot-clé) renvoie"Salut, Eve!"
4.3.1 Piège des Paramètres par Défaut Mutables
En Python, les valeurs par défaut des paramètres sont évaluées une seule fois au moment de la définition de la fonction, et non à chaque appel. Si l'on utilise un objet mutable (liste, dictionnaire, etc.) comme valeur par défaut, cet objet est partagé entre tous les appels qui ne fournissent pas explicitement cet argument.
def ajouter(element, liste=[]):
liste.append(element)
return liste
print(ajouter(1)) # [1]
print(ajouter(2)) # [1, 2] <- et non [2] !
print(ajouter(3)) # [1, 2, 3]
Pour éviter ce piège, on utilise None comme valeur par défaut et on initialise l'objet mutable à l'intérieur de la fonction :
def ajouter(element, liste=None):
if liste is None:
liste = []
liste.append(element)
return liste
4.3.2 Arguments Variables : *args et **kwargs
Il est possible de définir des fonctions acceptant un nombre variable d'arguments :
*args(arguments positionnels variables) : regroupe tous les arguments positionnels supplémentaires dans un tuple.**kwargs(arguments nommés variables) : regroupe tous les arguments nommés supplémentaires dans un dictionnaire.
def somme_tout(*args):
"""Calcule la somme d'un nombre quelconque d'arguments."""
return sum(args)
def affiche_infos(**kwargs):
"""Affiche les paires clé-valeur passées par mot-clé."""
for cle, valeur in kwargs.items():
print(f"{cle}: {valeur}")
print(somme_tout(1, 2, 3, 4, 5)) # 15
affiche_infos(nom="Alice", age=30, ville="Paris")
# Affiche :
# nom: Alice
# age: 30
# ville: Paris
4.3.3 Annotations de Type (Type Hints)
Depuis Python 3.5, il est possible d'annoter les paramètres et la valeur de retour d'une fonction. Les annotations ne sont pas vérifiées à l'écriture ou à l'exécution par l'interpréteur standard, mais servent à la documentation et à des outils d'analyse statique (mypy, IDE).
def factorielle(n: int) -> int:
"""Calcule n! pour n entier positif."""
res = 1
for i in range(2, n + 1):
res *= i
return res
def saluer(nom: str, message: str = "Bonjour") -> str:
return f"{message}, {nom}!"
4.4 Portée des Variables (Règle LEGB)
La portée d'une variable désigne la zone du code où son nom est accessible.
Lorsqu'un identifiant est référencé, Python recherche sa valeur selon l'ordre strict suivant :
- Local : À l'intérieur de la fonction courante.
- Enclosing : Dans les fonctions englobantes (si les fonctions sont imbriquées).
- Global : Au niveau du module principal (script global).
- Built-in : Noms prédéfinis par le langage (ex.
len,print).
Une variable définie hors d'une fonction est lisible dans celle-ci. Cependant, pour la modifier, il faut déclarer explicitement le mot-clé global en début de fonction, sous peine de créer une nouvelle variable locale homonyme.
compteur = 0
def incrementer():
global compteur
compteur += 1
def afficher():
print(compteur) # Lecture seule, global n'est pas nécessaire
incrementer()
afficher() # Affiche 1
4.5 Mutabilité et Passage d'Arguments
Python utilise un mécanisme de passage d'arguments décrit comme passage par référence d'objet (ou call by sharing). La distinction fondamentale est la suivante :
- Les types immuables (
int,float,str,tuple) : une modification dans la fonction crée un nouvel objet local, sans affecter l'original. - Les types mutables (
list,dict,set) : une modification de l'objet lui-même (via.append(),[]=, etc.) affecte l'original.
def modifie_immuable(x):
x = x + 1 # Crée un nouvel objet local
print(f"À l'intérieur : {x}")
def modifie_mutable(lst):
lst.append(4) # Modifie l'objet existant
print(f"À l'intérieur : {lst}")
a = 5
modifie_immuable(a) # Affiche : À l'intérieur : 6
print(f"À l'extérieur : {a}") # Affiche : À l'extérieur : 5 (inchangé)
b = [1, 2, 3]
modifie_mutable(b) # Affiche : À l'intérieur : [1, 2, 3, 4]
print(f"À l'extérieur : {b}") # Affiche : À l'extérieur : [1, 2, 3, 4] (modifié)
Pour éviter les effets de bord indésirables sur une liste passée en argument, on peut lui appliquer la copie superficielle (liste.copy() ou liste[:]) avant de la modifier.
4.6 Fonctions comme Objets de Première Classe
En Python, les fonctions sont des objets comme les autres. Elles peuvent être :
- Affectées à des variables,
- Passées en argument à d'autres fonctions,
- Retournées par d'autres fonctions,
- Stockées dans des structures de données.
Une fonction qui prend une autre fonction en argument ou en renvoie une est appelée fonction d'ordre supérieur.
def applique(f, x):
"""Applique la fonction f à l'argument x."""
return f(x)
def carre(y):
return y * y
def double(y):
return 2 * y
print(applique(carre, 5)) # 25
print(applique(double, 5)) # 10
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.