Les fonctions en profondeur : valeurs par défaut, fonctions comme objets, *args, **kwargs et lambda
- Définir des paramètres avec une valeur par défaut et appeler une fonction avec des arguments nommés
- Comprendre qu’une fonction est un objet — on peut la stocker dans une variable et la passer à une autre fonction
- Écrire des fonctions qui reçoivent un nombre variable d’arguments avec
*argset**kwargs - Écrire une lambda simple et l’utiliser avec
sorted(key=...)
Qu’allons-nous apprendre ?
Dans le thème code propre et débogage, nous avons appris les bases : def, les paramètres, return. Aujourd’hui, nous allons apprendre quelques capacités qui rendent les fonctions beaucoup plus souples :
- Les valeurs par défaut et les arguments nommés.
- Les fonctions comme objets — une fonction est une valeur comme une autre.
*args— autant d’arguments ordinaires que l’on veut.**kwargs— autant d’arguments nommés que l’on veut.- Lambda — une courte fonction sans nom.
Pourquoi en avons-nous besoin ?
print("a", "b", "c", sep="-")reçoit autant d’arguments que l’on veut, plus un argument nommé. Comment est-ce construit ? Avec*args.sorted(students, key=...)reçoit une fonction en paramètre, pour savoir selon quoi trier.- FastAPI (les thèmes backend et bases de données) est entièrement construit sur l’idée qu’une fonction est un objet : vous écrivez une fonction, et FastAPI l’« enregistre » comme Endpoint et l’exécute quand une requête arrive.
Explication à partir des bases
Les valeurs par défaut
On peut donner à un paramètre une valeur qui sera utilisée si aucun argument n’a été passé :
def greet(name, greeting="Hello"):
return greeting + ", " + namegreet("Dana") → "Hello, Dana". greet("Dana", "Hi") → "Hi, Dana".
Règle : les paramètres avec une valeur par défaut viennent après les paramètres sans valeur par défaut.
Les arguments nommés (Keyword Arguments)
Lors de l’appel, on peut indiquer le nom d’un paramètre, et alors l’ordre n’a plus d’importance :
greet(greeting="Hey", name="Avi")C’est très lisible quand il y a beaucoup de paramètres : create_user("dana", age=24, is_admin=False).
Les fonctions comme objets
En Python, une fonction est un objet, comme un nombre ou une liste. On peut donc :
- La stocker dans une variable :
say = greet. - La mettre dans une liste ou un dict.
- La passer en argument à une autre fonction.
La différence essentielle : greet (sans parenthèses) = la fonction elle-même. greet("Dana") (avec parenthèses) = son exécution, et son résultat.
greet est la recette : on peut la photographier et l’envoyer à un ami. greet("Dana") est le gâteau : le résultat de la cuisson selon la recette. Quand on passe une fonction à une autre fonction, on envoie la recette, pour qu’elle puisse cuisiner à partir d’elle quand elle en a besoin.
*args — un nombre variable d’arguments
Un astérisque devant le nom du paramètre dit : « rassemble tous les arguments ordinaires (par position) dans un tuple » :
def total(*numbers):
return sum(numbers)
total(1, 2) # numbers = (1, 2)
total(5, 10, 20, 1) # numbers = (5, 10, 20, 1)Le nom args est une convention. C’est l’astérisque qui fait le travail.
**kwargs — un nombre variable d’arguments nommés
Deux astérisques : « rassemble tous les arguments nommés dans un dict » :
def describe(**details):
for key, value in details.items():
print(key, "=", value)
describe(name="Dana", age=24, city="Haifa")
# details = {"name": "Dana", "age": 24, "city": "Haifa"}kwargs = keyword arguments.
Lambda
lambda est une façon d’écrire une petite fonction, en une ligne, sans nom :
lambda x: x * 2Équivalent à :
def double(x):
return x * 2- Après
lambda: les paramètres. Après:: une seule expression qui est la valeur renvoyée (sansreturn). - L’usage principal : passer une fonction courte en argument, au lieu de définir un
defséparé. Surtout avecsorted,min,max.
Syntaxe
# default value + keyword arguments
def func(required, optional="default"):
...
func("a")
func("a", optional="b")
# function as an object
f = func # no parentheses: the function itself
f("a") # call it through the new name
def apply(operation, value):
return operation(value) # call the function we received
# *args and **kwargs
def func(*args, **kwargs):
# args -> tuple of positional arguments
# kwargs -> dict of keyword arguments
...
# lambda
lambda parameters: expression
sorted(items, key=lambda item: item["price"])Premier exemple de code
week-2/functions_deep.py :
def make_greeting(name, greeting="Hello", punctuation="!"):
return greeting + ", " + name + punctuation
print(make_greeting("Dana"))
print(make_greeting("Avi", "Good morning"))
print(make_greeting("Noa", punctuation="?"))
def shout(text):
return text.upper()
def whisper(text):
return text.lower() + "..."
def say(message, style):
return style(message)
print(say("Hello there", shout))
print(say("Hello there", whisper))Sortie :
Hello, Dana!
Good morning, Avi!
Hello, Noa?
HELLO THERE
hello there...Analyse du code ligne par ligne
| Ligne | Explication |
|---|---|
def make_greeting(name, greeting="Hello", punctuation="!") | name est obligatoire. Les deux autres sont facultatifs, avec des valeurs par défaut. |
make_greeting("Dana") | Les deux paramètres facultatifs prennent leur valeur par défaut. |
make_greeting("Avi", "Good morning") | Un deuxième argument par position va dans greeting. |
make_greeting("Noa", punctuation="?") | Un argument nommé : on saute greeting (valeur par défaut) et on ne définit que punctuation. |
def say(message, style): | style est un paramètre qui attend une fonction. |
return style(message) | L’exécution de la fonction reçue. say ne sait pas et ne se soucie pas de quelle fonction il s’agit. |
say("Hello there", shout) | On passe shout sans parenthèses, c’est-à-dire la fonction elle-même. |
Un autre exemple
*args, **kwargs et lambda dans un petit système réel. week-2/store_tools.py :
def order_total(*prices, discount=0):
subtotal = sum(prices)
return subtotal - subtotal * discount / 100
print(order_total(10, 20, 30))
print(order_total(100, 50, discount=10))
def build_user(username, **extra):
user = {"username": username}
for key, value in extra.items():
user[key] = value
return user
print(build_user("dana"))
print(build_user("avi", age=30, city="Haifa", is_admin=True))
products = [
{"name": "Laptop", "price": 3500},
{"name": "Mouse", "price": 80},
{"name": "Desk", "price": 900},
]
by_price = sorted(products, key=lambda product: product["price"])
for product in by_price:
print(product["name"], product["price"])
cheapest = min(products, key=lambda product: product["price"])
print("Cheapest:", cheapest["name"])Sortie :
60.0
135.0
{'username': 'dana'}
{'username': 'avi', 'age': 30, 'city': 'Haifa', 'is_admin': True}
Mouse 80
Desk 900
Laptop 3500
Cheapest: MouseAnalyse :
def order_total(*prices, discount=0):—*pricesrassemble n’importe quel nombre de prix dans un tuple.discountvient après*prices, il ne peut donc être passé que par son nom (discount=10). Sinon, Python ne saurait pas où s’arrêtent les prix.build_user("avi", age=30, ...)—usernamereçoit"avi", et tout le reste est rassemblé dansextrasous forme de dict :{"age": 30, "city": "Haifa", "is_admin": True}.sorted(products, key=lambda product: product["price"])—sortedne sait pas comparer des dicts. Lekeyest une fonction qui lui dit : « pour chaque produit, utilise son prix pour comparer ». La lambda est appelée une fois pour chaque produit.min(..., key=...)— la même idée : « le minimum selon le prix ». Il renvoie le produit entier, pas seulement le prix.
Erreur courante
Erreur 1 : passer un résultat au lieu d’une fonction.
print(say("Hello", shout()))TypeError: shout() missing 1 required positional argument: 'text'shout() avec des parenthèses essaie d’exécuter shout immédiatement, sans argument. Quand vous passez une fonction, écrivez seulement son nom : shout.
Erreur 2 : une liste ou un dict comme valeur par défaut.
def add_item(item, cart=[]):
cart.append(item)
return cart
print(add_item("milk"))
print(add_item("bread"))['milk']
['milk', 'bread']Nous attendions ['bread'] au second appel ! La valeur par défaut [] est créée une seule fois, quand la fonction est définie, et tous les appels partagent la même liste (Mutable, la leçon précédente !). La correction en usage :
def add_item(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cartErreur 3 : une lambda trop compliquée.
result = sorted(users, key=lambda u: (0 if u["active"] else 1, -u["score"], u["name"].lower()))Cela fonctionne, mais personne ne le comprendra dans une semaine (KISS !). Si une lambda n’est pas lisible d’un coup d’œil, écrivez un def avec un nom clair.
Erreur 4 : un paramètre avec valeur par défaut avant un paramètre obligatoire.
def greet(greeting="Hello", name):SyntaxError: parameter without a default follows parameter with a defaultExercices
Les exercices n’utilisent que ce qui a été vu dans cette leçon et les précédentes. Essayez d’abord seul, et n’ouvrez la solution qu’ensuite.
Exercice 1: Valeurs par défaut et arguments nommés
Écrivez une fonction make_greeting(name, greeting="Hello", punct="!").
print(make_greeting("Dana"))
print(make_greeting("Avi", "Good morning"))
print(make_greeting("Noa", punct="?"))Résultat attendu :
Hello, Dana!
Good morning, Avi!
Hello, Noa?Afficher la solution
def make_greeting(name, greeting="Hello", punct="!"):
return greeting + ", " + name + punct
print(make_greeting("Dana"))
print(make_greeting("Avi", "Good morning"))
print(make_greeting("Noa", punct="?"))Hello, Dana!
Good morning, Avi!
Hello, Noa?Dans le troisième appel, punct="?" est passé par son nom, on peut donc sauter greeting et lui laisser sa valeur par défaut.
Exercice 2: *args
Écrivez une fonction average(*numbers) qui reçoit un nombre quelconque de nombres et renvoie la moyenne. Si aucun nombre n’a été passé — elle lance une ValueError avec le message indiqué dans le résultat attendu.
print(average(80, 90))
print(average(70, 80, 90, 100))
try:
average()
except ValueError as error:
print("error:", error)Résultat attendu :
85.0
85.0
error: need at least one numberAfficher la solution
def average(*numbers):
if len(numbers) == 0:
raise ValueError("need at least one number")
return sum(numbers) / len(numbers)
print(average(80, 90))
print(average(70, 80, 90, 100))
try:
average()
except ValueError as error:
print("error:", error)85.0
85.0
error: need at least one number*numbers rassemble tous les arguments par position dans un tuple.
Exercice 3: **kwargs
Écrivez une fonction format_profile(name, **details) qui renvoie une ligne avec le nom, suivi de chaque détail supplémentaire au format key=value.
print(format_profile("Dana", age=24, city="Haifa"))
print(format_profile("Avi"))Résultat attendu :
Dana: age=24 city=Haifa
Avi:Afficher la solution
def format_profile(name, **details):
line = name + ":"
for key, value in details.items():
line = line + " " + key + "=" + str(value)
return line
print(format_profile("Dana", age=24, city="Haifa"))
print(format_profile("Avi"))Dana: age=24 city=Haifa
Avi:**details rassemble tous les arguments nommés dans un dict.
Exercice 4: lambda dans un tri
products = [("Laptop", 3500), ("Mouse", 80), ("Desk", 900)]- Triez par prix, du moins cher au plus cher, et affichez chaque produit.
- Affichez le nom du produit le plus cher.
Résultat attendu :
Mouse 80
Desk 900
Laptop 3500
Most expensive: LaptopAfficher la solution
products = [("Laptop", 3500), ("Mouse", 80), ("Desk", 900)]
for name, price in sorted(products, key=lambda p: p[1]):
print(name, price)
most_expensive = max(products, key=lambda p: p[1])
print("Most expensive:", most_expensive[0])Mouse 80
Desk 900
Laptop 3500
Most expensive: Laptoplambda p: p[1] est une courte fonction qui renvoie le prix — sorted et max comparent selon elle.
Exercice 5: Une valeur par défaut mutable
def add_item(item, cart=[]):
cart.append(item)
return cart
print(add_item("milk"))
print(add_item("bread"))- Qu’est-ce qui sera affiché au second appel, et pourquoi ?
- Corrigez la fonction.
Afficher la solution
['milk', 'bread']sera affiché. La valeur par défaut[]est créée une seule fois, quand la fonction est définie — et tous les appels partagent la même liste.- Utilisez
None, et créez une nouvelle liste dans la fonction :
def add_item(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cart
print(add_item("milk"))
print(add_item("bread"))['milk']
['bread']Résumé
- Une valeur par défaut :
def f(a, b=10). Les paramètres avec une valeur par défaut viennent en dernier. Jamais[]ou{}comme valeur par défaut (utilisezNone). - Un argument nommé :
f(a=1, b=2). Lisible, et l’ordre n’a pas d’importance. - Une fonction est un objet :
f= la fonction,f()= son exécution. On peut la stocker dans une variable, dans un dict, et la passer en argument. *argsrassemble les arguments ordinaires dans un tuple.**kwargsrassemble les arguments nommés dans un dict.lambda x: expressionest une courte fonction sans nom. Parfaite pour lekey=desorted/min/max. Si elle est compliquée, undefest préférable.