Les arguments sont transmis par affectation . La raison derrière cela est double:
- le paramètre transmis est en fait une référence à un objet (mais la référence est passée par valeur)
- certains types de données sont modifiables, mais d'autres ne le sont pas
Donc:
Si vous passez un objet mutable dans une méthode, la méthode obtient une référence à ce même objet et vous pouvez le muter à votre plus grand plaisir, mais si vous reliez la référence dans la méthode, la portée externe n'en saura rien, et après vous avez terminé, la référence externe pointera toujours sur l'objet d'origine.
Si vous passez un objet immuable à une méthode, vous ne pouvez toujours pas lier la référence externe et vous ne pouvez même pas muter l'objet.
Pour le rendre encore plus clair, nous allons avoir quelques exemples.
Liste - un type mutable
Essayons de modifier la liste transmise à une méthode:
def try_to_change_list_contents(the_list):
print('got', the_list)
the_list.append('four')
print('changed to', the_list)
outer_list = ['one', 'two', 'three']
print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)
Production:
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Étant donné que le paramètre transmis est une référence à outer_list
, et non une copie de celui-ci, nous pouvons utiliser les méthodes de liste de mutation pour le modifier et faire en sorte que les modifications soient reflétées dans la portée externe.
Voyons maintenant ce qui se passe lorsque nous essayons de modifier la référence transmise en tant que paramètre:
def try_to_change_list_reference(the_list):
print('got', the_list)
the_list = ['and', 'we', 'can', 'not', 'lie']
print('set to', the_list)
outer_list = ['we', 'like', 'proper', 'English']
print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)
Production:
before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']
Étant donné que le the_list
paramètre a été transmis par valeur, l'attribution d'une nouvelle liste n'a eu aucun effet que le code en dehors de la méthode pouvait voir. Le the_list
était une copie de la outer_list
référence, et nous avons eu le the_list
point à une nouvelle liste, mais il n'y avait aucun moyen de changer l' endroit où outer_list
pointe.
String - un type immuable
Il est immuable, donc nous ne pouvons rien faire pour changer le contenu de la chaîne
Maintenant, essayons de changer la référence
def try_to_change_string_reference(the_string):
print('got', the_string)
the_string = 'In a kingdom by the sea'
print('set to', the_string)
outer_string = 'It was many and many a year ago'
print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)
Production:
before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago
Encore une fois, puisque le the_string
paramètre a été transmis par valeur, lui affecter une nouvelle chaîne n'a eu aucun effet que le code en dehors de la méthode pouvait voir. Le the_string
était une copie de la outer_string
référence, et nous avons eu le the_string
point d'une nouvelle chaîne, mais il n'y avait aucun moyen de changer l' endroit où outer_string
pointe.
J'espère que cela clarifie un peu les choses.
EDIT: Il a été noté que cela ne répond pas à la question à laquelle @David avait initialement demandé: "Y a-t-il quelque chose que je puisse faire pour transmettre la variable par référence réelle?". Travaillons là-dessus.
Comment contourner cela?
Comme le montre la réponse de @ Andrea, vous pouvez renvoyer la nouvelle valeur. Cela ne change pas la façon dont les choses sont transmises, mais vous permet d'obtenir les informations que vous souhaitez récupérer:
def return_a_whole_new_string(the_string):
new_string = something_to_do_with_the_old_string(the_string)
return new_string
# then you could call it like
my_string = return_a_whole_new_string(my_string)
Si vous vouliez vraiment éviter d'utiliser une valeur de retour, vous pouvez créer une classe pour conserver votre valeur et la transmettre à la fonction ou utiliser une classe existante, comme une liste:
def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
new_string = something_to_do_with_the_old_string(stuff_to_change[0])
stuff_to_change[0] = new_string
# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)
do_something_with(wrapper[0])
Bien que cela semble un peu lourd.