ParcoursOuvrir l'appli

La portée des variables et les tests

Python, les fonctions · 45 min

Ce qui se passe dans une fonction reste dans la fonction

def calculer():
    resultat = 42
    return resultat

calculer()
print(resultat)
NameError: name 'resultat' is not defined

Surprise : resultat a pourtant été créée ! Oui, mais à l'intérieur de la fonction.

Pour récupérer un résultat calculé dans une fonction, il n'y a qu'un moyen propre : le renvoyer avec return, et le ranger à l'appel :

resultat = calculer()

Deux variables, même nom

x = 10
def changer():
    x = 99         # crée une NOUVELLE variable x, locale
changer()
print(x)           # 10 : le x global n'a pas bougé

Dans la fonction, x = 99 crée une variable locale qui porte le même nom. Elle cache la globale pendant l'appel, puis disparaît. La globale reste intacte.

Python permet de modifier une globale depuis une fonction avec le mot-clé global. Évite-le : c'est une source de bugs très difficiles à trouver, comme le montre l'histoire suivante.

Documenter : les docstrings

def prix_ttc(prix_ht, taux=0.1925):
    """Renvoie le prix toutes taxes comprises, arrondi au franc.

    prix_ht : le prix hors taxes, en FCFA.
    taux : le taux de TVA, 19,25 % par défaut (Cameroun).
    """
    return round(prix_ht * (1 + taux))

Les trois guillemets permettent d'écrire un texte sur plusieurs lignes. Contrairement à un commentaire #, la docstring est gardée par Python : help(prix_ttc) l'affiche. Les éditeurs comme VS Code la montrent quand tu survoles un appel.

Une bonne docstring dit quoi et pour quoi, pas comment : le code dit déjà comment.

Tester ses fonctions avec `assert`

Depuis le début, un correcteur vérifie tes exercices en appelant tes fonctions avec plusieurs valeurs. Les développeurs professionnels font exactement pareil avec leur propre code : ils écrivent des tests.

def prix_ttc(prix_ht, taux=0.1925):
    return round(prix_ht * (1 + taux))

assert prix_ttc(1000) == 1192
assert prix_ttc(0) == 0
assert prix_ttc(1000, 0.05) == 1050
print("Tous les tests passent")

Si un jour quelqu'un modifie prix_ttc et la casse, les tests le diront immédiatement, avant que la bêtise n'arrive chez les clients.

Exercice : Répare : la variable perdue

Ce programme veut afficher le total d'une commande, mais il plante avec une NameError. Corrige-le sans utiliser le mot global : la fonction doit renvoyer son résultat.

Voir une solution
def calculer_total(prix):
    total = 0
    for p in prix:
        total += p
    return total


total = calculer_total([500, 1200, 300])
print(f"Total : {total} FCFA")

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Exercice : Écris toi-même les tests

La fonction est_bissextile(annee) est donnée. Écris en dessous au moins quatre assert qui la testent : 2024 (oui), 2026 (non), 1900 (non), 2000 (oui). Puis affiche Tous les tests passent.

Ensuite, pour voir un test échouer, remplace volontairement 400 par 300 dans la fonction et relance : lis l'AssertionError. Remets 400 avant de vérifier.

Voir une solution
def est_bissextile(annee):
    """Renvoie True si l'année est bissextile (calendrier grégorien)."""
    return (annee % 4 == 0 and annee % 100 != 0) or annee % 400 == 0


assert est_bissextile(2024)
assert not est_bissextile(2026)
assert not est_bissextile(1900)
assert est_bissextile(2000)
print("Tous les tests passent")

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Exercice : D'abord le test, ensuite le code

Fais comme Kent Beck. Les tests de nettoyer_telephone(numero) sont déjà écrits en bas. Ils échouent, puisque la fonction ne fait encore rien. Écris la fonction pour qu'ils passent tous.

Elle doit renvoyer le numéro sans espaces, sans tirets et sans points, et sans l'indicatif +237 du Cameroun s'il est présent au début.

Voir une solution
def nettoyer_telephone(numero):
    """Renvoie le numéro sans séparateurs ni indicatif +237."""
    numero = numero.replace(" ", "").replace("-", "").replace(".", "")
    if numero.startswith("+237"):
        numero = numero[4:]
    return numero


assert nettoyer_telephone("699 12 34 56") == "699123456"
assert nettoyer_telephone("699-12-34-56") == "699123456"
assert nettoyer_telephone("+237 699 12 34 56") == "699123456"
assert nettoyer_telephone("+237699.12.34.56") == "699123456"
print("Tous les tests passent")

Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice

Teste-toi

Qu'affiche ce code ? python total = 0 def ajouter(n): total = n ajouter(5) print(total)
  1. 5
  2. 0 (bonne réponse)
  3. Une erreur
  4. None

Dans la fonction, total = n crée une variable locale. La variable globale total reste à 0. Pour obtenir 5, il faut que la fonction renvoie la valeur et qu'on la range : total = ajouter(5) avec un return.