Les arguments sont transmis par affectation . La raison de cette décision est double :
- le paramètre transmis est en fait un référence à un objet (mais la référence est passée par valeur)
- certains types de données sont mutables, mais d'autres ne le sont pas.
Donc :
-
Si vous passez un mutable Mais si vous liez à nouveau la référence dans la méthode, la portée externe n'en saura rien, et une fois que vous aurez terminé, la référence externe pointera toujours vers l'objet original.
-
Si vous passez un immuable à une méthode, vous ne pouvez toujours pas relier la référence externe, et vous ne pouvez même pas muter l'objet.
Pour que cela soit encore plus clair, prenons quelques exemples.
Liste - un type mutable
Essayons de modifier la liste qui a été 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)
Sortie :
before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']
Puisque le paramètre transmis est une référence à outer_list
et non une copie de celle-ci, nous pouvons utiliser les méthodes de la liste de mutation pour la modifier et faire en sorte que les changements soient reflétés dans la portée externe.
Voyons maintenant ce qui se passe lorsque nous essayons de modifier la référence qui a été 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)
Sortie :
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']
Depuis le the_list
est transmis par valeur, l'affectation d'une nouvelle liste n'a aucun effet visible pour le code extérieur à la méthode. Le site the_list
était une copie de la outer_list
référence, et nous avions the_list
pointent vers une nouvelle liste, mais il n'y avait aucun moyen de changer l'endroit où outer_list
pointu.
String - un type immuable
C'est immuable, donc il n'y a rien que nous puissions faire pour changer le contenu de la chaîne de caractères
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)
Sortie :
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
Là encore, puisque le the_string
est transmis par valeur, l'affectation d'une nouvelle chaîne de caractères n'a aucun effet visible pour le code extérieur à la méthode. Le site the_string
était une copie de la outer_string
référence, et nous avions the_string
vers une nouvelle chaîne, mais il n'y avait aucun moyen de changer l'endroit où les outer_string
pointu.
J'espère que cela clarifie un peu les choses.
EDITAR: Il a été noté que cela ne répond pas à la question que @David a posée à l'origine, "Y a-t-il quelque chose que je puisse faire pour passer la variable par référence réelle ?". Travaillons sur ce point.
Comment contourner ce problème ?
Comme le montre la réponse d'@Andrea, vous pouvez retourner la nouvelle valeur. Cela ne change pas la façon dont les données sont transmises, mais vous permet de récupérer les informations que vous souhaitez :
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 voulez vraiment éviter d'utiliser une valeur de retour, vous pouvez créer une classe pour contenir votre valeur et la passer dans 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.
31 votes
Pour une brève explication/clarification, voir la première réponse à la question suivante cette question de stackoverflow . Les chaînes de caractères étant immuables, elles ne seront pas modifiées et une nouvelle variable sera créée, de sorte que la variable "extérieure" aura toujours la même valeur.
11 votes
Le code de la réponse de BlairConrad est bon, mais l'explication fournie par DavidCournapeau et DarenThomas est correcte.
79 votes
Avant de lire la réponse choisie, pensez à lire ce court texte Les autres langues ont des "variables", Python a des "noms". . Pensez à des "noms" et à des "objets" plutôt qu'à des "variables" et à des "références" et vous devriez éviter beaucoup de problèmes similaires.
28 votes
Pourquoi cela utilise-t-il inutilement une classe ? Ce n'est pas Java :P
2 votes
Une autre solution consiste à créer une "référence" enveloppante comme ceci : ref = type('', (), {'n':1}) stackoverflow.com/a/1123054/409638
1 votes
Pour les noms globaux, le passage par référence peut être simulé en passant le nom comme une chaîne de caractères et en utilisant globals().
def change(s): globals()[s] = 'changed'
suivi para = 'orig'; change('a'); print(a)
imprime'changed'
.0 votes
Quelques types immuables : {int, float, long, complex, str, bytes, tuple, frozen set} Quelques types mutables : {tableau d'octets, liste, ensemble, dict}
1 votes
Python a des variables. Il n'y a aucun problème conceptuel avec ce terme, qui est d'ailleurs couramment utilisé.
0 votes
Le blog de jeff Knupp y stupidpythonideas Il y a de bonnes explications à cela.
0 votes
Pour le cadre de données nommé 'bob' (à partir d'une variable de type chaîne, et c'est là que le bât blesse), donnez-moi son contenu actuel. Cela devrait être facile. Malheureusement, je n'y arrive pas.
2 votes
@lqc votre lien n'est pas accessible
2 votes
Lien fonctionnel : Les autres langues ont des "variables", Python a des "noms".
10 votes
Nouveau mode officiel de liaison de l'Iqc : david.goodger.org/projets/pycon/2007/idiomatic/
0 votes
Je viens du C# et j'essaie de mieux comprendre la question. En Python, tout est un objet et les objets peuvent être mutables ou immuables. Le code ici utilise "string" qui est un objet immuable et tenter de le modifier dans le cadre de la fonction ne changera pas la valeur de l'appelant (se comporte comme pass-by-value) & si le code utilisait un objet de classe personnalisée mutable - changer dans le cadre de la fonction changera la valeur de l'appelant (se comporte comme pass-by-ref). Vous verrez exactement le même résultat final en C# (mais la méthode utilisée est pass-by-value/ref). L'effet net n'est-il pas le même ? La question n'est-elle pas donc discutable ?
0 votes
@PeterR parce que OOP > 70s programmation sans classes et objets. C'est juste plus facile d'écrire et de comprendre des programmes quand ils sont orientés objet. Je pense que tout dans l'Univers peut être représenté comme un objet, même inexistant dans les concepts de programmation de la vie réelle.