Python, en ce qui me concerne, est exactement comme Java, dans la mesure où toutes les fonctions (méthodes) sont de type "pass-by-value". Voir cette fameuse réponse . L'inconvénient de "l'appel par partage" est qu'il vous fait penser que quelque chose de différent se produit pour différents types (c'est-à-dire que les types mutables se comportent différemment dans les appels de fonction que les types immuables). Ce n'est pas du tout le cas, la même chose se produit chaque fois que vous appelez une fonction. De plus, aucun manuel que j'ai lu ne mentionne le concept, je pense que pass-by-value et pass-by-reference sont des terminologies beaucoup plus populaires.
Comment fonctionne le passage par la valeur ?
Elle fonctionne en copiant la "valeur" qui a été transmise à la fonction avant de la modifier dans la fonction. Prenons l'exemple suivant :
my_int = 4
def pass_by_value(value):
value += 1
return value
print pass_by_value(my_int)
print my_int
cette sortie :
5
4
Observez que la valeur de my_int
n'a pas changé . Il est resté à 4
même si la fonction a incrémenté la valeur qui a été transmise pour être 5
. C'est parce qu'il a été copié. Il y a là plusieurs subtilités sur lesquelles nous reviendrons. Soyez indulgent avec moi.
Passer par valeur sur une liste
my_list = [1,2,3]
def pass_by_value_list(li):
li.append(4)
return li
print pass_by_value_list(my_list)
print my_list
cette sortie :
[1, 2, 3, 4]
[1, 2, 3, 4]
Qu'est-ce que c'est que ça ? La valeur de my_list
a changé. Vous venez de dire plus haut que la méthode "pass-by-value" copie la valeur ! Par conséquent, my_list
n'aurait pas dû changer !!!
Le premier exemple était trop subtil. Je n'ai pas défini correctement la "valeur" copiée. Il s'avère que la valeur copiée est pas les données mais plutôt, l'endroit où les données sont stockées . En général, les programmeurs C/C++ appellent cela un pointeur o adresse . Reprenons les exemples, mais modifions-les un peu pour mieux comprendre.
Comment fonctionne le passage par la valeur ? Version 2.0 :
my_int = 4
def pass_by_value(value):
print "Address of parameter before += is: ", id(value)
value += 1
print "Address of parameter after += is: ", id(value)
return value
print "Address of parameter outside of the function is: ", id(my_int)
pass_by_value(my_int)
Lorsque j'ai exécuté cette opération, j'ai obtenu le résultat suivant :
Address of parameter outside of the function is: 40592528
Address of parameter before += is: 40592528
Address of parameter after += is: 40592504
On dirait que l'adresse du paramètre avant l'élément +=
était 40592528. Il s'agit également de la même "valeur" que celle du paramètre extérieur à la fonction ! Mais après l'opérateur +=
la "valeur" a changé à l'intérieur la fonction ! Cependant, ce changement d'adresse n'a pas été propagé à l'extérieur la fonction, car le adresse es pass-by-value . La nouvelle adresse à l'intérieur de la fonction est 40592504 (différente de 40592528, bien que proche). Pour faire court, la fonction +=
crée un nouveau int
et n'opère pas sur les données de l'adresse transmise à la fonction.
Passage par valeur sur une liste, Version 2.0 :
my_list = [1,2,3]
def pass_by_value_list(li):
print "Address of parameter before .append() is: ", id(li)
li.append(4)
print "Address of parameter after .append() is: ", id(li)
return li
print "Address of parameter outside of the function is: ", id(my_list)
pass_by_value_list(my_list)
cette sortie :
Address of parameter outside of the function is: 110841160
Address of parameter before .append() is: 110841160
Address of parameter after .append() is: 110841160
C'est différent du cas des nombres entiers. On dirait que toutes les adresses sont les mêmes ! En effet, cela diffère du cas des nombres entiers. Les append
opère sur une liste en place et ne renvoie pas une nouvelle liste. C'est la raison pour laquelle vous voyez un changement dans my_list
en dehors de la fonction. append
modifie la liste à l'adresse qui a été transmise à la fonction, de sorte que les données sont modifiées partout dans le programme. Ce qui n'a pas changé, en revanche, c'est l'adresse.
Observez que :
my_list = [1,2,3]
def dont_change_the_list(li):
li.append(4)
li = []
return li
print dont_change_the_list(my_list)
print my_list
sorties
[]
[1, 2, 3, 4]
En d'autres termes, la modification de la liste à l'intérieur de la fonction n'a pas été propagée à l'extérieur de la fonction, même si cela semblait être le comportement que nous avions observé auparavant . En effet, l'énoncé li = []
modifie l'adresse de li
mais nous avons copié l'adresse avant l'exécution de la fonction. L'adresse du paramètre est la "valeur" qui est copiée, et il n'est pas possible de la modifier à l'intérieur d'une fonction. . Cela n'a donc pas modifié les données dans my_list
dans le programme principal.
L'essentiel : Python utilise toujours la sémantique "pass-by-value" pour ses appels de fonction. La valeur est simplement l'adresse de l'objet qui a été passé, et non les données de l'objet qui a été passé.