Tout ce que vous avez écrit jusqu'ici était un script : une recette qui s'exécute du début à la fin en une seule fois. Cette leçon vous fait passer à la construction d'outils. Une fonction est un bloc de code nommé qui accepte des entrées, accomplit une tâche précise et renvoie un résultat. Elle transforme votre façon de programmer plus que toute autre fonctionnalité. Les fonctions vous évitent de copier-coller, permettent de tester une logique de manière isolée, de lire un programme comme une table des matières plutôt qu'un mur de lignes, et, à partir de la partie 6, de construire les classes et modules qui composent les vrais logiciels.
Il y a aussi un avantage plus discret qui mérite d'être souligné. Les fonctions vous obligent à décider de ce que fait réellement un morceau de code, car vous devez le nommer et définir ce qui entre et ce qui sort. Les développeurs découvrent la moitié de leurs erreurs de conception au moment de nommer. Si vous ne pouvez pas nommer honnêtement une fonction sans utiliser le mot « et », c'est probablement deux fonctions. Cette règle en une phrase améliorera votre code pendant la prochaine décennie.
Ce que vous apprendrez dans la partie 4
- Définir des fonctions avec def, les appeler et renvoyer des valeurs
- Paramètres, arguments, valeurs par défaut et arguments nommés
- Retours anticipés, valeurs de retour multiples et ce que None signifie vraiment
- Portée locale contre portée globale, et pourquoi les variables globales causent des problèmes
- Signatures flexibles avec *args et **kwargs
- Docstrings et petites fonctions anonymes avec lambda
Remarque
Avant de commencer
Vous avez besoin des conditions de la partie 2 et des boucles de la partie 3, car les fonctions d'exemple encapsulent exactement ce type de logique. Si vous pouvez écrire FizzBuzz de mémoire, vous êtes prêt.
1. Définir et appeler une fonction
Une définition de fonction commence par def, puis le nom, puis une liste de paramètres entre parenthèses, puis deux-points et un corps indenté, la même règle de bloc que vous maîtrisez depuis la partie 2. L'exécution de l'instruction def n'exécute pas le corps ; elle crée la fonction et lie le nom. Le corps ne s'exécute que lorsque vous appelez la fonction, avec des parenthèses, et chaque appel est une exécution fraîche avec des variables fraîches. L'instruction return met fin à l'appel immédiatement et renvoie une valeur à l'appelant.
def grade_for(score):
"""Return the letter grade for a 0-100 score."""
if score >= 75:
return "A"
elif score >= 65:
return "B"
elif score >= 50:
return "C"
elif score >= 35:
return "S"
return "F"
print(grade_for(73)) # B
print(grade_for(91)) # A
result = grade_for(40) # the returned value can be stored, printed, compared
print(result) # S
Voici l'échelle de notes de la partie 2, mais remarquez comment la version avec fonction l'améliore. La logique a maintenant un nom, elle peut être réutilisée pour un millier d'étudiants sans copier une seule ligne, et les retours anticipés aplatissent l'échelle de elif : une fois qu'un return s'exécute, l'appel est terminé, donc chaque branche peut simplement énoncer sa réponse. Cette chaîne entre triples guillemets sur la première ligne est une docstring, la manière standard de documenter ce que fait une fonction ; les éditeurs l'affichent au survol, et help(grade_for) l'imprime. Une phrase honnête par fonction est une habitude professionnelle qui coûte cinq secondes.
2. Paramètres, valeurs par défaut et arguments nommés
Les noms dans la ligne def sont des paramètres ; les valeurs que vous passez lors de l'appel sont des arguments. Python les associe par position par défaut, mais vous pouvez aussi passer n'importe quel argument par nom, ce qui rend les appels auto-documentés, et vous pouvez donner des valeurs par défaut aux paramètres, ce qui les rend optionnels. Les valeurs par défaut doivent venir après les paramètres sans valeur par défaut dans la signature, et la combinaison de ces deux fonctionnalités couvre la plupart des besoins de flexibilité du monde réel.
def make_greeting(name, punctuation="!", lang="en"):
if lang == "si":
return f"Ayubowan, {name}{punctuation}"
return f"Hello, {name}{punctuation}"
print(make_greeting("Amina")) # Hello, Amina!
print(make_greeting("Amina", "?")) # Hello, Amina?
print(make_greeting("Amina", lang="si")) # Ayubowan, Amina!
print(make_greeting(punctuation=".", name="Zane")) # keyword order is free
Un avertissement mérite son propre encadré car il piège tout le monde exactement une fois : n'utilisez jamais une valeur mutable comme une liste comme valeur par défaut. Les valeurs par défaut sont évaluées une seule fois, au moment de la définition de la fonction, donc une liste par défaut est partagée par tous les appels qui s'y fient, et les éléments s'accumulent mystérieusement. L'idiome est de mettre None par défaut et de créer la liste à l'intérieur de la fonction. Vous comprendrez pleinement le mécanisme après la partie 5 ; pour l'instant, adoptez la règle.
# Wrong: the default list is created ONCE and shared
def add_item_bad(item, basket=[]):
basket.append(item)
return basket
# Right: make a fresh list per call
def add_item(item, basket=None):
if basket is None:
basket = []
basket.append(item)
return basket
print(add_item_bad("apple")) # ['apple']
print(add_item_bad("mango")) # ['apple', 'mango'] <- surprise!
print(add_item("apple")) # ['apple']
print(add_item("mango")) # ['mango'] <- correct
Point de contrôle
Une fonction a la signature def f(a, b=2, c=3). Quel appel est invalide ?
3. Retours, None et renvoyer plusieurs choses
Chaque appel de fonction produit une valeur. Si le corps n'exécute jamais de return, ou exécute un return nu sans rien après, la valeur est None, le marqueur d'absence que vous avez rencontré dans la partie 1. Cela explique une confusion classique : print renvoie None, donc x = print("hi") affiche le texte et laisse x contenir None. Afficher est quelque chose qu'une fonction fait ; renvoyer est ce qu'elle retourne. Gardez ces deux notions séparées dans votre tête et toute une catégorie de bogues de débutant disparaît.
Les fonctions Python peuvent aussi renvoyer plusieurs valeurs à la fois en les listant après return, séparées par des virgules. Techniquement, cela renvoie un tuple, que vous rencontrerez formellement dans la partie 5, mais le côté appelant se lit élégamment dans les deux cas : vous dépaquetez les résultats dans des noms en une seule ligne. Les fonctions qui calculent deux choses liées, comme un minimum et un maximum, ou un quotient et un reste, utilisent cela partout dans le vrai code.
def min_max(numbers):
lowest = highest = numbers[0]
for n in numbers:
if n < lowest:
lowest = n
if n > highest:
highest = n
return lowest, highest
low, high = min_max([13, 4, 27, 9, 18])
print(f"range: {low} to {high}") # range: 4 to 27
4. Portée : où vivent les noms
Les variables affectées à l'intérieur d'une fonction sont locales : elles sont créées au début de l'appel et disparaissent à sa fin. La fonction ne peut pas écraser accidentellement les variables de votre script, et deux fonctions peuvent toutes deux utiliser un nom comme total sans entrer en collision. Lire une variable externe depuis l'intérieur d'une fonction fonctionne, mais lui affecter une valeur crée à la place une nouvelle variable locale, ce qui est précisément le comportement qui vous protège. Python fournit bien un mot-clé global pour forcer l'affectation à des noms de niveau module, et le conseil professionnel est sans appel : ne l'utilisez pas. Les fonctions qui communiquent via des paramètres et des valeurs de retour peuvent être comprises et testées isolément ; les fonctions qui vont modifier un état partagé ne le peuvent pas.
tax_rate = 0.12 # module level, "global" to this file
def price_with_tax(price):
total = price * (1 + tax_rate) # reading a global: fine
return round(total, 2)
print(price_with_tax(1000)) # 1120.0
# print(total) # NameError: 'total' lives only inside the call
La lecture de variables globales a toutefois une forme légitime et quotidienne : les constantes. Les valeurs qui configurent votre programme et ne changent jamais, un taux d'imposition, un chemin de fichier, une limite de tentatives, se placent conventionnellement en haut du fichier en MAJUSCULES_AVEC_TIRETS_BAS, comme TAUX_TVA = 0.12, et les fonctions les lisent librement. Les majuscules sont une promesse faite à chaque lecteur que rien ne réaffecte ce nom, ce qui explique pourquoi les lire depuis des fonctions est honnête tandis que leur affecter une valeur est le péché. La plupart des fichiers Python bien tenus commencent par les imports, puis les constantes, puis les fonctions, une organisation que vos futurs projets devraient reproduire.
5. Signatures flexibles : *args et **kwargs
Il arrive qu'une fonction doive accepter un nombre quelconque d'arguments ; print en est l'exemple évident. Un paramètre écrit *args collecte tous les arguments positionnels supplémentaires dans un tuple, et **kwargs collecte les arguments nommés supplémentaires dans un dictionnaire. Vous en écrirez occasionnellement et en lirez constamment, car les bibliothèques les utilisent pour transmettre des options aux couches inférieures. Pour l'instant, un seul exemple honnête suffit à démystifier les astérisques pour toujours.
def average(*numbers):
if not numbers: # truthiness, Part 2
return 0.0
return sum(numbers) / len(numbers)
print(average(4, 8)) # 6.0
print(average(1, 2, 3, 4, 5)) # 3.0
print(average()) # 0.0
L'image miroir de *args est le déballage sur le site d'appel, et cela complète le tableau : si vos valeurs se trouvent déjà dans une liste ou un dictionnaire, une étoile les déploie dans un appel. numbers = [4, 8, 15] puis average(*numbers) passe trois arguments distincts, et options = {"punctuation": "!", "lang": "si"} avec make_greeting("Amina", **options) remplit les paramètres nommés à partir du dict. Vous rencontrerez cela constamment dans le code des bibliothèques, où les fonctions transmettent tout ce qu'elles ont reçu à une autre fonction avec une seule paire *args, **kwargs.
Une idée supplémentaire doit trouver sa place dans votre modèle des fonctions avant que les lambdas n'aient un sens : en Python, les fonctions sont des valeurs. Un def crée un objet, le nom est une variable ordinaire qui pointe dessus, et vous pouvez stocker des fonctions dans des listes, les passer en arguments et les renvoyer depuis d'autres fonctions. Ce n'est pas une astuce exotique ; c'est ainsi que sorted vous permet de personnaliser l'ordre et que la moitié de la bibliothèque standard accepte un comportement en paramètre. La syntaxe lambda ci-dessous existe uniquement pour rendre le passage d'une petite fonction moins cérémonieux.
6. Lambdas : de petites fonctions anonymes
Une lambda est une fonction sans nom et avec une expression unique pour corps : lambda x: x * 2 est l'équivalent jetable d'un def de deux lignes. Les lambdas existent pour une raison principale : être passées comme arguments à des fonctions qui attendent un peu de logique, et l'exemple canonique est le tri par clé. sorted accepte une fonction key qu'il appelle sur chaque élément pour décider de l'ordre, et une lambda est la manière naturelle de dire trier par le deuxième élément ou trier par longueur, en ligne, sur le site d'appel. Si une lambda dépasse une courte expression, promouvez-la en un def avec un nom ; la lisibilité l'emporte toujours.
students = [("Amina", 87), ("Zane", 91), ("Ruwan", 78)]
by_score = sorted(students, key=lambda s: s[1], reverse=True)
print(by_score)
# [('Zane', 91), ('Amina', 87), ('Ruwan', 78)]
words = ["banana", "fig", "apple"]
print(sorted(words, key=len)) # ['fig', 'apple', 'banana']
Point de contrôle
Que laisse x = print("hello") dans x ?
7. Pratique : remanier un script en fonctions
Le bac à sable ci-dessous contient le calculateur d'impôts de la partie 2 réécrit sous forme de fonctions, ainsi qu'un calculateur de pourboire avec un bug délibéré. Vos exercices : corrigez le bug en lisant la trace d'appel, ajoutez un argument par défaut et extrayez une fonction supplémentaire. Remanier du code qui fonctionne pour lui donner une forme plus propre représente la moitié de la programmation professionnelle, et c'est une compétence que vous ne pouvez acquérir qu'en faisant exactement cela.
Remarquez l'architecture même dans cet exemple simplifié : tax_rate_for gère la règle métier, tax_due gère le calcul, et chacune peut évoluer sans toucher à l'autre. Quand la partie 13 introduira les tests automatisés, des fonctions comme celles-ci seront triviales à tester, précisément parce qu'elles prennent des entrées et renvoient des sorties sans effet de bord. Une bonne structure aujourd'hui, c'est des tests gratuits demain.
! Erreurs fréquentes à éviter
-
✕Appeler une fonction avant de l'avoir définie et obtenir une NameError.
✓Python lit les fichiers de haut en bas ; le def doit être exécuté avant l'appel. Disposition standard : toutes les définitions d'abord, puis le code qui les utilise.
-
✕Oublier les parenthèses : écrire result = my_func au lieu de my_func().
✓Sans parenthèses, vous obtenez l'objet fonction lui-même, pas son résultat. Si vous voyez quelque chose comme <function my_func at 0x...>, vous avez oublié de l'appeler.
-
✕Utiliser une liste ou un dictionnaire comme valeur par défaut d'un paramètre.
✓Les valeurs par défaut sont évaluées une seule fois, à la définition, et partagées entre tous les appels. Mettez None par défaut et créez l'objet frais à l'intérieur du corps de la fonction.
-
✕Compter sur print à l'intérieur des fonctions au lieu de return, puis être incapable d'utiliser le résultat.
✓Calculez et renvoyez dans la fonction ; affichez à l'endroit de l'appel. Les fonctions qui renvoient sont réutilisables et testables ; celles qui se contentent d'afficher sont des impasses.
? Questions fréquentes
Quelle longueur doit avoir une fonction ? +
Assez courte pour être nommée honnêtement. Beaucoup de professionnels visent un écran ou moins, mais le vrai test est de savoir si le nom décrit tout ce que fait le corps. Dès qu'une fonction a besoin d'un « et » dans son nom, scindez-la.
Quelle est la différence entre paramètres et arguments ? +
Les paramètres sont les noms dans la ligne def ; les arguments sont les valeurs réelles fournies lors de l'appel. La distinction est importante pour lire les messages d'erreur, qui utilisent les deux termes avec précision.
Quand utiliser lambda plutôt que def ? +
Uniquement pour passer une fonction d'une seule expression en argument, typiquement comme clé de tri ou filtre. Tout ce que vous appelleriez plus d'une fois, ou qui nécessite une deuxième ligne, mérite un def et un nom.
Les fonctions peuvent-elles s'appeler elles-mêmes ? +
Oui, c'est la récursivité, et Python la prend en charge avec une limite de profondeur par défaut d'environ mille appels. C'est élégant pour les problèmes en forme d'arbre, et vous la rencontrerez naturellement quand nous parcourrons les structures de dossiers dans la partie 8.
8. Récapitulatif et suite
Vous savez désormais définir des fonctions, passer des arguments par position et par mot-clé, fixer des valeurs par défaut sûres, renvoyer une ou plusieurs valeurs, raisonner sur la portée locale, accepter des arguments variables, documenter avec des docstrings et utiliser les lambdas là où il faut. À partir de maintenant dans ce cours, chaque exemple sera organisé en fonctions, car c'est ainsi que l'on écrit du vrai Python, et vos scripts sont officiellement devenus des programmes.
Voici la leçon que beaucoup d'apprenants considèrent comme le moment où Python devient clair : Partie 5, les structures de données en Python, qui couvre les listes, les tuples, les ensembles, les dictionnaires et les compréhensions qui font la renommée de Python. La leçon et le quiz sur les fonctions dans l'application Learn Python ci-dessous vous offrent des exercices supplémentaires sur le sujet du jour, et le programme complet se trouve sur le hub de la série.
Conseil de pro
Après avoir écrit une fonction, appelez-la immédiatement deux fois avec des arguments différents, dont un cas limite comme zéro ou une chaîne vide. Trente secondes de test rapide trouvent les bogues qu'une heure de relecture laisse passer, et c'est exactement l'état d'esprit qui deviendra le test automatisé dans la partie 13.
Entraînez-vous en mobilité
Learn Python, l'application Android gratuite
Chaque sujet de cette série est aussi dans l'application : leçons concises, exemples exécutables, quiz, mini-projets et un environnement Python hors ligne qui fonctionne sur votre téléphone.
Commentaires
0Aucun commentaire pour l’instant. Soyez la première personne à donner votre avis.