20. Paramètres et arguments de fonction
Dans le Chapitre 19, nous avons appris à définir et appeler des fonctions avec des paramètres de base. Maintenant, nous allons explorer en profondeur le système flexible de paramètres et d’arguments de Python. Comprendre ces mécanismes vous permet d’écrire des fonctions à la fois puissantes et faciles à utiliser.
20.1) Arguments positionnels et nommés
Lorsque vous appelez une fonction, vous pouvez passer des arguments de deux façons fondamentales : par position ou par nom (mot-clé/keyword).
20.1.1) Arguments positionnels
Les arguments positionnels sont associés aux paramètres en fonction de leur ordre. Le premier argument va au premier paramètre, le deuxième au deuxième paramètre, et ainsi de suite.
def calculate_discount(price, discount_percent):
"""Calculate the final price after applying a discount."""
discount_amount = price * (discount_percent / 100)
final_price = price - discount_amount
return final_price
# Passage des arguments par position
result = calculate_discount(100, 20)
print(result)Sortie :
80.0Dans cet exemple, 100 est affecté à price et 20 à discount_percent uniquement en fonction de leurs positions dans l’appel de fonction.
L’ordre compte de manière critique avec les arguments positionnels :
# Exemple : nous voulons calculer un article à 100 $ avec 20 % de remise
# Bon ordre : price d'abord, puis discount
print(calculate_discount(100, 20))
# Mauvais ordre : discount d'abord, puis price
print(calculate_discount(20, 100))Sortie :
80.0
-16.0Lorsque vous inversez les arguments, Python ne sait pas que vous avez fait une erreur — il les affecte simplement dans l’ordre. Cela produit un résultat mathématiquement valide mais logiquement incorrect (un prix négatif !).
20.1.2) Arguments nommés
Les arguments nommés spécifient explicitement quel paramètre reçoit quelle valeur en utilisant le nom du paramètre suivi d’un signe égal et de la valeur. Cela rend votre code plus lisible et protège contre les erreurs d’ordre.
def create_user_profile(username, email, age):
"""Create a user profile with the given information."""
profile = f"User: {username}\nEmail: {email}\nAge: {age}"
return profile
# Utilisation d'arguments nommés
profile = create_user_profile(username="alice_smith", email="alice@example.com", age=28)
print(profile)Sortie :
User: alice_smith
Email: alice@example.com
Age: 28Avec les arguments nommés, l’ordre n’a pas d’importance :
# Même résultat, ordre différent
profile1 = create_user_profile(username="bob", email="bob@example.com", age=35)
profile2 = create_user_profile(age=35, username="bob", email="bob@example.com")
profile3 = create_user_profile(email="bob@example.com", age=35, username="bob")
# Les trois produisent des résultats identiques
print(profile1 == profile2 == profile3)Sortie :
TrueCette flexibilité est particulièrement précieuse lorsqu’une fonction a de nombreux paramètres, ce qui permet de voir facilement quelle valeur correspond à quel paramètre.
20.1.3) Mélanger arguments positionnels et arguments nommés
Vous pouvez combiner les deux styles dans un seul appel de fonction, mais il y a une règle importante : les arguments positionnels doivent venir avant les arguments nommés.
def format_address(street, city, state, zip_code):
"""Format a mailing address."""
return f"{street}\n{city}, {state} {zip_code}"
# Valide : arguments positionnels d'abord, puis arguments nommés
address = format_address("123 Main St", "Springfield", state="IL", zip_code="62701")
print(address)Sortie :
123 Main St
Springfield, IL 62701Ici, "123 Main St" et "Springfield" sont positionnels (affectés à street et city), tandis que state et zip_code sont spécifiés par nom.
Tenter de placer des arguments positionnels après des arguments nommés provoque une erreur :
# Invalide : argument positionnel après un argument nommé
# address = format_address(street="123 Main St", "Springfield", state="IL", zip_code="62701")
# SyntaxError: positional argument follows keyword argumentPython impose cette règle car une fois que vous commencez à utiliser des arguments nommés, il devient ambigu de savoir quel paramètre positionnel un argument non nommé, placé ensuite, devrait remplir.
20.1.4) Quand utiliser chaque style
Utilisez des arguments positionnels quand :
- La fonction a peu de paramètres (généralement 1 à 3)
- L’ordre des paramètres est évident et intuitif
- La fonction est couramment utilisée et l’ordre est bien connu
# Évident et concis
print(len("hello"))
result = max(10, 20, 5)Utilisez des arguments nommés quand :
- La fonction a de nombreux paramètres
- Le sens des paramètres n’est pas immédiatement évident
- Vous voulez ignorer certains paramètres qui ont des valeurs par défaut (abordé ensuite)
- Vous voulez rendre votre code auto-documenté
# Clair et explicite
user = create_user_profile(username="charlie", email="charlie@example.com", age=42)20.2) Valeurs par défaut des paramètres
Les fonctions peuvent spécifier des valeurs par défaut pour des paramètres. Lorsqu’un appelant ne fournit pas d’argument pour un paramètre qui a une valeur par défaut, Python utilise la valeur par défaut à la place.
20.2.1) Définir des paramètres avec des valeurs par défaut
Les valeurs par défaut sont spécifiées dans la définition de la fonction en utilisant l’opérateur d’affectation :
def greet_user(name, greeting="Hello"):
"""Greet a user with a customizable greeting."""
return f"{greeting}, {name}!"
# Utilisation de la salutation par défaut
print(greet_user("Alice"))
# Fournir une salutation personnalisée
print(greet_user("Bob", "Good morning"))
print(greet_user("Carol", greeting="Hi"))Sortie :
Hello, Alice!
Good morning, Bob!
Hi, Carol!Le paramètre greeting a une valeur par défaut de "Hello". Quand vous appelez greet_user("Alice"), Python utilise cette valeur par défaut. Quand vous fournissez un deuxième argument, il remplace la valeur par défaut.
20.2.2) Les paramètres avec valeurs par défaut doivent venir après les paramètres obligatoires
Python exige que les paramètres avec des valeurs par défaut apparaissent après tous les paramètres sans valeurs par défaut. Cette règle évite l’ambiguïté quant à la correspondance entre arguments et paramètres.
# Correct : paramètres obligatoires d'abord, puis défauts
def create_product(name, price, category="General", in_stock=True):
"""Create a product record."""
return {
"name": name,
"price": price,
"category": category,
"in_stock": in_stock
}
product = create_product("Laptop", 999.99)
print(product)Sortie :
{'name': 'Laptop', 'price': 999.99, 'category': 'General', 'in_stock': True}Tenter de placer un paramètre obligatoire après un paramètre ayant une valeur par défaut provoque une erreur de syntaxe :
# Invalide : paramètre obligatoire après un paramètre par défaut
# def invalid_function(name="Unknown", age):
# return f"{name} is {age} years old"
# SyntaxError: non-default argument follows default argumentCela a du sens : si name a une valeur par défaut mais que age n’en a pas, comment Python saurait-il si invalid_function(25) signifie name=25 avec age manquant, ou age=25 avec name utilisant sa valeur par défaut ? La règle élimine cette ambiguïté.
20.2.3) Usages pratiques des paramètres par défaut
Les paramètres par défaut sont excellents pour les fonctions où certains arguments changent rarement :
def calculate_shipping(weight, distance, express=False):
"""Calculate shipping cost based on weight and distance."""
base_rate = 0.50 * weight + 0.10 * distance
if express:
base_rate *= 2 # Les frais de livraison express sont doublés
return round(base_rate, 2)
# La plupart des expéditions sont standard
standard_cost = calculate_shipping(5, 100)
print(f"Standard: ${standard_cost}")
# Parfois, quelqu'un a besoin de l'express
express_cost = calculate_shipping(5, 100, express=True)
print(f"Express: ${express_cost}")Sortie :
Standard: $12.5
Express: $25.0Cette conception rend le cas le plus courant (livraison standard) simple à appeler, tout en prenant en charge le cas moins fréquent (express) lorsque c’est nécessaire.
20.2.4) Plusieurs valeurs par défaut et remplacement sélectif
Quand une fonction a plusieurs paramètres avec des valeurs par défaut, vous pouvez remplacer n’importe quelle combinaison en utilisant des arguments nommés :
def format_currency(amount, currency="USD", show_symbol=True, decimal_places=2):
"""Format a number as currency."""
symbols = {"USD": "$", "EUR": "€", "GBP": "£", "JPY": "¥"}
formatted = f"{amount:.{decimal_places}f}"
if show_symbol and currency in symbols:
formatted = f"{symbols[currency]}{formatted}"
return formatted
# Utiliser tous les défauts
print(format_currency(42.5))
# Remplacer seulement la devise
print(format_currency(42.5, currency="EUR"))
# Remplacer plusieurs défauts
print(format_currency(42.5, currency="JPY", decimal_places=0))Sortie :
$42.50
€42.50
¥42Cette flexibilité permet aux appelants de personnaliser exactement ce dont ils ont besoin tout en gardant l’appel de fonction concis.
20.3) Listes d’arguments de longueur variable avec *args
Parfois, vous voulez qu’une fonction accepte un nombre quelconque d’arguments sans savoir à l’avance combien il y en aura. Python fournit *args à cet effet.
20.3.1) Comprendre *args
La syntaxe *args dans une liste de paramètres collecte tous les arguments positionnels supplémentaires dans un tuple. Le nom args est une convention (abréviation de « arguments »), mais vous pouvez utiliser n’importe quel nom de paramètre valide après l’astérisque.
def calculate_total(*numbers):
"""Calculate the sum of any number of values."""
total = 0
for num in numbers:
total += num
return total
# Fonctionne avec n'importe quel nombre d'arguments
print(calculate_total(10))
print(calculate_total(10, 20))
print(calculate_total(10, 20, 30, 40))
print(calculate_total())Sortie :
10
30
100
0À l’intérieur de la fonction, numbers est un tuple contenant tous les arguments positionnels passés à la fonction. Quand aucun argument n’est fourni, c’est un tuple vide.
20.3.2) Combiner des paramètres classiques avec *args
Vous pouvez avoir des paramètres classiques avant *args. Les paramètres classiques consomment les premiers arguments, et *args collecte le reste :
def create_team(team_name, *members):
"""Create a team with a name and any number of members."""
member_list = ", ".join(members)
return f"Team {team_name}: {member_list}"
# Le premier argument va à team_name, le reste va à members
print(create_team("Alpha", "Alice", "Bob"))
print(create_team("Beta", "Carol"))
print(create_team("Gamma", "Dave", "Eve", "Frank", "Grace"))Sortie :
Team Alpha: Alice, Bob
Team Beta: Carol
Team Gamma: Dave, Eve, Frank, GraceLe premier argument ("Alpha", "Beta", ou "Gamma") est affecté à team_name, et tous les arguments restants sont collectés dans le tuple members.
20.4) Paramètres uniquement nommés et paramètres **kwargs
Python fournit deux mécanismes supplémentaires pour gérer les arguments : les paramètres uniquement nommés et **kwargs pour collecter des arguments nommés arbitraires.
20.4.1) Paramètres uniquement nommés
Les paramètres uniquement nommés doivent être spécifiés en utilisant des arguments nommés — ils ne peuvent pas être passés de manière positionnelle. Vous les créez en les plaçant après un * ou après *args dans la liste de paramètres.
def create_account(username, *, email, age):
"""Create an account. Email and age must be specified by name."""
return {
"username": username,
"email": email,
"age": age
}
# Correct : email et age spécifiés par nom
account = create_account("alice", email="alice@example.com", age=28)
print(account)
# Invalide : tentative de passer email et age de façon positionnelle
# account = create_account("bob", "bob@example.com", 30)
# TypeError: create_account() takes 1 positional argument but 3 were givenSortie :
{'username': 'alice', 'email': 'alice@example.com', 'age': 28}Le * dans la liste de paramètres agit comme un séparateur. Tout ce qui se trouve après doit être passé comme un argument nommé. C’est utile lorsque vous voulez forcer les appelants à être explicites sur certains paramètres, ce qui rend le code plus lisible et moins sujet aux erreurs.
Vous pouvez aussi combiner des paramètres classiques, *args, et des paramètres uniquement nommés :
def log_event(event_type, *details, severity="INFO", timestamp=None):
"""Log an event with optional details and metadata."""
# Nous apprendrons le module datetime en détail au Chapitre 39,
# mais pour l'instant, retenez simplement que ces lignes obtiennent l'heure actuelle
# et la formatent sous forme de chaîne de caractères représentant un timestamp
from datetime import datetime
if timestamp is None:
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
details_str = " | ".join(details)
return f"[{timestamp}] {severity}: {event_type} - {details_str}"
# event_type est positionnel, details sont collectés par *details,
# severity et timestamp sont keyword-only
print(log_event("Login", "User: alice", "IP: 192.168.1.1"))
print(log_event("Error", "Database connection failed", severity="ERROR"))Sortie (le timestamp variera selon le moment où vous exécutez le code) :
[2025-12-18 19:29:16] INFO: Login - User: alice | IP: 192.168.1.1
[2025-12-18 19:29:16] ERROR: Error - Database connection failed20.4.2) Comprendre **kwargs
La syntaxe **kwargs collecte tous les arguments nommés supplémentaires dans un dictionnaire. Comme args, le nom kwargs est conventionnel (abréviation de « keyword arguments »), mais vous pouvez utiliser n’importe quel nom valide après le double astérisque.
def create_product(**attributes):
"""Create a product with any number of attributes."""
product = {}
for key, value in attributes.items():
product[key] = value
return product
# Passez tous les arguments nommés que vous voulez
laptop = create_product(name="Laptop", price=999.99, brand="TechCorp", in_stock=True)
print(laptop)
phone = create_product(name="Phone", price=699.99, color="Black")
print(phone)Sortie :
{'name': 'Laptop', 'price': 999.99, 'brand': 'TechCorp', 'in_stock': True}
{'name': 'Phone', 'price': 699.99, 'color': 'Black'}À l’intérieur de la fonction, attributes est un dictionnaire dont les clés sont les noms des paramètres et les valeurs sont les arguments passés.
20.4.3) Combiner paramètres classiques, *args et **kwargs
Vous pouvez utiliser tous ces mécanismes ensemble, mais ils doivent apparaître dans un ordre spécifique :
- Paramètres positionnels classiques
*args(si présent)- Paramètres uniquement nommés (si présents)
**kwargs(si présent)
def complex_function(required, *args, keyword_only, **kwargs):
"""Demonstrate all parameter types together."""
print(f"Required: {required}")
print(f"Args: {args}")
print(f"Keyword-only: {keyword_only}")
print(f"Kwargs: {kwargs}")
complex_function(
"value1", # required
"value2", "value3", # args
keyword_only="kw", # keyword_only
extra1="e1", # kwargs
extra2="e2" # kwargs
)Sortie :
Required: value1
Args: ('value2', 'value3')
Keyword-only: kw
Kwargs: {'extra1': 'e1', 'extra2': 'e2'}Cette flexibilité est puissante, mais doit être utilisée avec discernement. La plupart des fonctions n’ont pas besoin de tous ces mécanismes.
20.4.4) Cas d’usage pratique : fonctions de configuration
Un usage courant de **kwargs consiste à créer des fonctions qui acceptent des options de configuration :
def connect_to_database(host, port, **options):
"""Connect to a database with flexible configuration options."""
connection_string = f"Connecting to {host}:{port}"
# Traiter toutes les options supplémentaires
if options.get("ssl"):
connection_string += " with SSL"
if options.get("timeout"):
connection_string += f" (timeout: {options['timeout']}s)"
if options.get("pool_size"):
connection_string += f" (pool size: {options['pool_size']})"
return connection_string
# Connexion de base
print(connect_to_database("localhost", 5432))
# Avec SSL
print(connect_to_database("db.example.com", 5432, ssl=True))
# Avec plusieurs options
print(connect_to_database("db.example.com", 5432, ssl=True, timeout=30, pool_size=10))Sortie :
Connecting to localhost:5432
Connecting to db.example.com:5432 with SSL
Connecting to db.example.com:5432 with SSL (timeout: 30s) (pool size: 10)Ce modèle permet à la fonction d’accepter n’importe quel nombre de paramètres de configuration optionnels sans devoir tous les définir explicitement dans la liste de paramètres.
20.5) Déballage des arguments lors de l’appel des fonctions
Tout comme *args et **kwargs collectent des arguments lorsqu’on définit des fonctions, vous pouvez utiliser * et ** pour déballer des collections lors de l’appel d’une fonction.
20.5.1) Déballer des séquences avec *
L’opérateur * déballe une séquence (liste, tuple, etc.) en arguments positionnels séparés :
def calculate_rectangle_area(width, height):
"""Calculate the area of a rectangle."""
return width * height
# Au lieu de passer les arguments individuellement
dimensions = [5, 10]
area = calculate_rectangle_area(dimensions[0], dimensions[1])
print(area)
# Déballer la liste directement
area = calculate_rectangle_area(*dimensions)
print(area)Sortie :
50
50Quand vous écrivez *dimensions, Python déballe la liste [5, 10] en deux arguments séparés, comme si vous aviez écrit calculate_rectangle_area(5, 10).
Cela fonctionne avec n’importe quel itérable :
def format_name(first, middle, last):
"""Format a full name."""
return f"{first} {middle} {last}"
# Déballage d'un tuple
name_tuple = ("John", "Q", "Public")
print(format_name(*name_tuple))
# Déballage d'une liste
name_list = ["Jane", "M", "Doe"]
print(format_name(*name_list))
# Même déballage d'une chaîne (chaque caractère devient un argument)
# Cela ne fonctionne que si la fonction attend le bon nombre d'arguments
def show_first_three(a, b, c):
return f"{a}, {b}, {c}"
print(show_first_three(*"ABC"))Sortie :
John Q Public
Jane M Doe
A, B, C20.5.2) Déballer des dictionnaires avec **
L’opérateur ** déballe un dictionnaire en arguments nommés :
def create_user(username, email, age):
"""Create a user profile."""
return f"User: {username}, Email: {email}, Age: {age}"
# Dictionnaire avec des clés correspondant aux noms des paramètres
user_data = {
"username": "alice",
"email": "alice@example.com",
"age": 28
}
# Déballer le dictionnaire
profile = create_user(**user_data)
print(profile)Sortie :
User: alice, Email: alice@example.com, Age: 28Quand vous écrivez **user_data, Python déballe le dictionnaire en arguments nommés, équivalent à :
create_user(username="alice", email="alice@example.com", age=28)Les clés du dictionnaire doivent correspondre aux noms des paramètres de la fonction, sinon vous obtiendrez une erreur :
# Invalide : la clé du dictionnaire ne correspond pas à un nom de paramètre
invalid_data = {"name": "bob", "email": "bob@example.com", "age": 30}
# profile = create_user(**invalid_data)
# TypeError: create_user() got an unexpected keyword argument 'name'20.5.3) Combiner le déballage avec des arguments classiques
Vous pouvez mélanger des arguments déballés avec des arguments classiques :
def calculate_total(base_price, tax_rate, discount):
"""Calculate total price after tax and discount."""
subtotal = base_price * (1 + tax_rate)
total = subtotal * (1 - discount)
return round(total, 2)
# Certains arguments classiques, certains déballés
pricing = [0.08, 0.10] # tax_rate et discount
total = calculate_total(100, *pricing)
print(total)Sortie :
97.2Vous pouvez aussi déballer plusieurs collections dans un seul appel :
def create_full_address(street, city, state, zip_code, country):
"""Create a complete address."""
return f"{street}, {city}, {state} {zip_code}, {country}"
street_address = ["123 Main St", "Springfield"]
location_details = ["IL", "62701", "USA"]
address = create_full_address(*street_address, *location_details)
print(address)Sortie :
123 Main St, Springfield, IL 62701, USA20.5.4) Exemple pratique : appels de fonctions flexibles
Le déballage est particulièrement utile lorsqu’on travaille avec des données issues de sources externes :
def send_email(recipient, subject, body, cc=None, bcc=None):
"""Send an email with optional CC and BCC."""
message = f"To: {recipient}\nSubject: {subject}\n\n{body}"
if cc:
message += f"\nCC: {cc}"
if bcc:
message += f"\nBCC: {bcc}"
return message
# Données d'e-mail provenant d'un fichier de configuration ou d'une base de données
email_config = {
"recipient": "user@example.com",
"subject": "Welcome",
"body": "Thank you for signing up!",
"cc": "manager@example.com"
}
# Déballer la configuration directement
result = send_email(**email_config)
print(result)Sortie :
To: user@example.com
Subject: Welcome
Thank you for signing up!
CC: manager@example.comCe modèle facilite le fait de faire circuler des arguments de fonction sous forme de structures de données, ce qui est courant lors de la création d’API ou du traitement de fichiers de configuration.
20.6) Le piège des arguments par défaut mutables (pourquoi les valeurs par défaut de liste persistent)
L’un des pièges les plus notoires de Python implique l’utilisation d’objets mutables (comme les listes ou les dictionnaires) comme valeurs par défaut de paramètres. Comprendre ce problème est crucial pour écrire des fonctions correctes.
20.6.1) Le problème : des valeurs par défaut mutables partagées
Considérez cette fonction apparemment innocente :
def add_student(name, grades=[]):
"""Add a student with their grades."""
grades.append(name)
return grades
# Premier appel
students1 = add_student("Alice")
print(students1)
# Deuxième appel - on s'attend à une liste nouvelle
students2 = add_student("Bob")
print(students2)
# Troisième appel
students3 = add_student("Carol")
print(students3)Sortie :
['Alice']
['Alice', 'Bob']
['Alice', 'Bob', 'Carol']Ce comportement surprend beaucoup de programmeurs. Chaque appel à add_student() sans fournir d’argument grades utilise le même objet liste, pas une nouvelle. La liste persiste entre les appels de fonction, en accumulant les valeurs.
20.6.2) Pourquoi cela arrive : les valeurs par défaut sont créées une seule fois
La clé pour comprendre ce comportement est de savoir quand les valeurs par défaut sont créées. Python évalue les valeurs par défaut des paramètres une seule fois, au moment où la fonction est définie, et non à chaque appel de la fonction.
def demonstrate_default_creation():
"""Show when defaults are created."""
print("Function defined!")
def use_default(value=demonstrate_default_creation()):
"""Use a default that calls a function."""
return value
# Le message s'affiche quand la fonction est DÉFINIE, pas appeléeSortie :
Function defined!Quand Python rencontre la ligne def use_default, il évalue le paramètre par défaut value=demonstrate_default_creation(). Cela appelle demonstrate_default_creation(), qui affiche immédiatement "Function defined!". Les appels ultérieurs à use_default() ne réévaluent pas ce défaut, donc rien d’autre ne s’affiche.
Quand Python rencontre def add_student(name, grades=[]):, il crée un objet liste vide et le stocke comme valeur par défaut pour grades. Chaque appel suivant qui ne fournit pas d’argument grades utilise ce même objet liste.
Voici une démonstration plus claire en utilisant l’identité des objets :
def show_list_identity(items=[]):
"""Show that the same list object is reused."""
print(f"List ID: {id(items)}")
items.append("item")
return items
# Chaque appel utilise le même objet liste (même ID)
show_list_identity()
show_list_identity()
show_list_identity()Sortie :
List ID: 140234567890123
List ID: 140234567890123
List ID: 140234567890123Les numéros d’ID exacts varieront selon votre système, mais notez que les trois appels montrent la même valeur d’ID, ce qui prouve qu’ils utilisent le même objet liste. La fonction id() renvoie un identifiant unique pour chaque objet en mémoire — lorsque les ID correspondent, c’est le même objet.
20.6.3) Le bon modèle : utiliser None par défaut
La solution standard consiste à utiliser None comme valeur par défaut et à créer un nouvel objet mutable dans la fonction :
def add_student_correct(name, grades=None):
"""Add a student with their grades (correct version)."""
if grades is None:
grades = [] # Créer une NOUVELLE liste à chaque fois
grades.append(name)
return grades
# Maintenant, chaque appel obtient sa propre liste
students1 = add_student_correct("Alice")
print(students1)
students2 = add_student_correct("Bob")
print(students2)
students3 = add_student_correct("Carol")
print(students3)Sortie :
['Alice']
['Bob']
['Carol']Ce modèle fonctionne parce que None est immuable et qu’une nouvelle liste est créée dans le corps de la fonction à chaque fois que grades est None.
20.6.4) Le même problème avec les dictionnaires
Ce problème affecte tous les types mutables, pas seulement les listes :
# WRONG: Dictionary default
def create_config_wrong(key, value, config={}):
"""Create a configuration (BUGGY VERSION)."""
config[key] = value
return config
config1 = create_config_wrong("theme", "dark")
print(config1)
config2 = create_config_wrong("language", "en")
print(config2)
print("---")
# CORRECT: None as default
def create_config_correct(key, value, config=None):
"""Create a configuration (CORRECT VERSION)."""
if config is None:
config = {}
config[key] = value
return config
config1 = create_config_correct("theme", "dark")
print(config1)
config2 = create_config_correct("language", "en")
print(config2)Sortie :
{'theme': 'dark'}
{'theme': 'dark', 'language': 'en'}
---
{'theme': 'dark'}
{'language': 'en'}20.6.5) Résumé : la règle d’or
N’utilisez jamais d’objets mutables (listes, dictionnaires, ensembles) comme valeurs par défaut de paramètres. Utilisez toujours None et créez l’objet mutable à l’intérieur de la fonction :
# ❌ WRONG
def function(items=[]):
pass
# ✅ CORRECT
def function(items=None):
if items is None:
items = []
# Maintenant, utilisez items en toute sécuritéCe modèle garantit que chaque appel de fonction obtient son propre objet mutable indépendant, évitant des bugs mystérieux où des données fuient entre les appels.
Dans ce chapitre, nous avons exploré en profondeur le système flexible de paramètres et d’arguments de Python. Vous avez appris à utiliser des arguments positionnels et nommés, à fournir des valeurs par défaut, à gérer un nombre variable d’arguments avec *args et **kwargs, à déballer des collections lors de l’appel de fonctions, et à éviter le piège des arguments par défaut mutables.
Ces mécanismes vous donnent des outils puissants pour concevoir des interfaces de fonctions à la fois flexibles et faciles à utiliser. À mesure que vous écrirez davantage de fonctions, vous développerez une intuition quant aux modèles de paramètres qui conviennent le mieux à différentes situations. La clé est de trouver un équilibre entre flexibilité et clarté — faites en sorte que vos fonctions soient faciles à appeler correctement et difficiles à appeler incorrectement.