Comprendre comment Python passe des arguments aux fonctions est crucial pour éviter des modifications inattendues des données et pour concevoir du code correctement.
Dans les langages de programmation traditionnels, tels que C ou Java, le passage par valeur (copy by value) ou par référence (copy by reference) est utilisé. Cependant, Python adopte un modèle différent : appel par référence d'objet (parfois appelé "appel par partage").
De nombreux développeurs pensent à tort que Python passe toujours des arguments par référence ou par valeur. Cela conduit inévitablement à des situations où des objets modifiables sont modifiés de manière inattendue dans le code appelant.
En Python, les valeurs des paramètres de fonction sont des références aux objets qui sont passés à la fonction. Cela signifie :
Exemple :
# list - modifiable (mutable) def add_item(lst): lst.append(42) my_list = [1, 2, 3] add_item(my_list) print(my_list) # [1, 2, 3, 42] # int - immuable (immutable) def add_num(x): x = x + 1 num = 10 add_num(num) print(num) # 10
Caractéristiques clés :
Les arguments sont-ils toujours passés par référence en Python ?
Non, en Python, ce sont des références aux objets qui sont passées, et le comportement de l'objet dépend de sa nature, modifiable ou non. Un objet immuable créera un nouvel objet en cas de modification.
Peut-on réaffecter un argument modifiable dans une fonction et affecter l'objet externe ?
Non. Si vous réaffectez une nouvelle valeur au paramètre à l'intérieur de la fonction, l'objet externe ne changera pas — vous ne faites que changer la référence locale.
Exemple :
def reassign_list(lst): lst = [99, 100] my_list = [1, 2, 3] reassign_list(my_list) print(my_list) # [1, 2, 3]
Pourquoi une fonction qui prend une liste par défaut peut-elle se comporter de manière étrange lors d'appels répétés ?
Parce que la valeur par défaut est créée une fois — lors de la définition de la fonction, et si elle est modifiée (par exemple, en ajoutant un élément), elle changera pour tous les appels suivants.
def add_element(x, cache=[]): cache.append(x) return cache print(add_element(1)) # [1] print(add_element(2)) # [1, 2]
Un programmeur passe une liste à une fonction et s'attend à ce que sa liste d'origine ne change pas, mais la fonction ajoute un élément.
Avantages :
Inconvénients :
Un programmeur copie explicitement la liste à l'intérieur de la fonction, s'il a besoin de renvoyer quelque chose, mais de ne pas modifier l'original :
def process_data(data): data = data.copy() # ou list(data) # travail sécurisé avec la copie data.append('rapport') return data
Avantages :
Inconvénients :