Adloun

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.

Définition 4.1Syntaxe d'une fonction

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
Exemple 4.2Calcul d'une factorielle

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__.

iRemarqueConvention PEP 257

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.

Définition 4.3Arguments nommés et valeurs par défaut

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}!"
Exemple 4.4Appels variés
  • 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.

Exemple 4.5Comportement surprenant

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]
iRemarqueCorrection idiomatique

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 :

Exemple 4.6Usage de *args et **kwargs

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).

Exemple 4.7Annotations de type

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.

Définition 4.8Règle LEGB

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).
iRemarqueVariables globales en lecture et écriture

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.

Exemple 4.9global en action

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 :

Exemple 4.10Mutabilité en action

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é)
iRemarqueConséquence importante

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 :

Définition 4.11Fonction d'ordre supérieur

Une fonction qui prend une autre fonction en argument ou en renvoie une est appelée fonction d'ordre supérieur.

Exemple 4.12Fonction comme argument

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 cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →