2541 votes

Comment puis-je passer une variable par référence?

La documentation Python semble pas clair quant à savoir si les paramètres sont passés par référence ou la valeur, et le code suivant donne la valeur inchangée "Original"

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change(self.variable)
        print self.variable

    def Change(self, var):
        var = 'Changed'

Est-il quelque chose que je peux faire pour passer la variable par référence?

2734voto

Blair Conrad Points 56195

Les Arguments sont transmis par cession. La justification de cette est double:

  1. le paramètre passé est en fait une référence à un objet (mais la référence est passée par valeur)
  2. certains types de données sont mutables, mais d'autres ne le sont pas

Donc:

  • Si vous passez une mutable objet dans une méthode, la méthode renvoie une référence à ce même objet et vous pouvez les transformer à votre guise, mais si vous réassocier la référence à la méthode, à l'extérieur de la portée ne sait rien sur elle, et après vous avez terminé, la référence externe sera toujours le point à l'objet d'origine.

  • Si vous passez un immuable objet à une méthode, vous ne pouvez pas relier la référence externe, et vous ne pouvez même pas la mutation de l'objet.

Pour le rendre encore plus clair, nous allons donner quelques exemples.

Liste - type mutable

Nous allons essayer de modifier la liste qui a été transmis à 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']

Depuis le paramètre transmis est une référence à l' outer_list, pas une copie de celui-ci, nous pouvons utiliser la mutation de la liste des méthodes pour changer et avoir des changements indiqués dans l'extérieur de la portée.

Maintenant, nous allons voir ce qui se passe quand on essaie de changer la référence qui a été passé en 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 l' the_list paramètre est passé par valeur, l'attribution d'une nouvelle liste, il n'a eu aucun effet que le code en dehors de la méthode pouvait voir. L' the_list était une copie de l' outer_list de référence, et nous avons eu the_list point d'une nouvelle liste, mais il n'y avait aucun moyen pour modifier l'emplacement de outer_list pointu.

Chaîne - un type immuable

Il est immuable, donc il n'y a rien que nous pouvons 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

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

Encore une fois, depuis l' the_string paramètre est passé par valeur, l'attribution d'une nouvelle chaîne, il n'a eu aucun effet que le code en dehors de la méthode pouvait voir. L' the_string était une copie de l' outer_string de référence, et nous avons eu the_string point d'une nouvelle liste, mais il n'y avait aucun moyen pour modifier l'emplacement de outer_string pointu.

J'espère que cela efface les choses un peu.

EDIT: Il a été noté que ce n'est pas répondre à la question que @David initialement demandé, "Est-il quelque chose que je peux faire pour passer la variable par référence réel?". Nous allons travailler là-dessus.

Comment contourner ce problème?

@Andrea réponse de la montre, vous pourriez revenir à la nouvelle valeur. Cela ne change pas la façon dont les choses se sont passées, mais ne vous permettent 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 tu voulais vraiment éviter d'utiliser une valeur de retour, vous pouvez créer une classe afin de conserver votre valeur et la passer dans la fonction ou l'utilisation d'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.

664voto

Mark Ransom Points 132545

Le problème vient d'une incompréhension de ce que sont les variables en Python. Si vous avez utilisé pour la plupart des langues, vous avez un modèle mental de ce qui se passe dans la séquence suivante:

a = 1
a = 2

Vous croyez qu' a est un emplacement de mémoire qui stocke la valeur 1, puis est mis à jour pour stocker la valeur 2. Ce n'est pas la façon dont les choses fonctionnent en Python. Plutôt, a commence comme une référence à un objet avec la valeur 1, puis se réaffecté comme une référence à un objet avec la valeur 2. Ces deux objets peuvent continuer à coexister, même si a ne fait pas référence à la première; en fait, ils peuvent être partagés par un certain nombre d'autres références au sein du programme.

Lorsque vous appelez une fonction avec un paramètre, une nouvelle référence est créé, qui se réfère à l'objet passé en. Ceci est distinct de la référence qui a été utilisé dans l'appel de la fonction, donc il n'y a aucun moyen de mise à jour de la référence et de faire référence à un nouvel objet. Dans votre exemple:

    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable est une référence à l'objet de type string 'Original'. Lorsque vous appelez Change vous créer une deuxième référence var à l'objet. À l'intérieur de la fonction, vous pouvez réaffecter la référence var pour un autre objet de type string 'Changed', mais la référence self.variable est distinct et ne change pas.

La seule façon de contourner cela est de faire passer un objet mutable. Parce que les deux références se rapportent au même objet, toute modification de l'objet sont reflétées dans les deux endroits.

    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

233voto

David Cournapeau Points 21956

Il n'est ni passer par valeur ou par référence - c'est l'appel-par-objet. Voir, par Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

Voici une importante citation:

"...les variables [nom] sont pas des objets; ils ne peuvent pas être repéré par d'autres variables ou visés par des objets."

Dans votre exemple, lorsque l' Change méthode est appelée--un espace de noms est créé pour elle; var devient un nom, dans cet espace, de l'objet de type string 'Original'. L'objet a alors un nom dans deux espaces de noms. Ensuite, var = 'Changed' lie var d'un nouvel objet de type string, et ainsi, la méthode de l'espace de noms oublie 'Original'. Enfin, cet espace de nom est oublié, et la chaîne 'Changed' avec elle.

166voto

Daren Thomas Points 26812

Penser à des choses transmis par cession plutôt que par référence/par valeur. De cette façon, il est toujours clair, ce qui se passe tant que vous comprenez ce qui se passe pendant le fonctionnement normal de l'affectation.

Ainsi, lors du passage d'une liste à une fonction/méthode, la liste est attribué le nom du paramètre. L'ajout à la liste de résultat dans la liste en cours de modification. Réaffectation des de la liste à l'intérieur de la fonction ne changera pas la liste originale, puisque:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

Depuis immuable types ne peuvent pas être modifiés, ils semblent comme être passé par valeur - passage d'un int dans une fonction signifie que l'attribution de l'int pour les fonctions de paramètre. Vous ne pouvez jamais réaffecter, mais il ne changera pas le originial variables de valeur.

59voto

pepr Points 4263

Techniquement, Python utilise toujours passer par des valeurs de référence. Je vais répéter ma réponse à faire à l'appui de mon affirmation.

Python utilise toujours passer par des valeurs de référence. Il n'y a pas d'exception. Toute affectation de variable signifie copie de la valeur de référence. Pas d'exception. Toute variable est le nom lié à la valeur de référence. Toujours.

Vous pouvez penser à une valeur de référence, comme l'adresse de l'objet cible. L'adresse est automatiquement déréférencé. De cette façon, en travaillant avec la valeur de référence, il semble que vous travaillez directement avec l'objet cible. Mais il y a toujours une référence entre les deux, un pas de plus pour passer à la cible.

Voici l'exemple qui prouve que Python utilise le passage par référence:

Illustrated example of passing the argument

Si l'argument est passé par valeur, à l'extérieur lst ne peut pas être modifiée. Le vert sont les objets cibles (le noir est la valeur stockée à l'intérieur, le rouge est le type d'objet), le jaune est la mémoire avec la valeur de référence à l'intérieur, tirés comme la flèche. Le bleu solide flèche correspond à la valeur de référence a été passé à la fonction (par l'intermédiaire de la flèche bleue en pointillés). Le vilain jaune foncé est le dictionnaire interne. (En fait, il pourrait être attiré comme une ellipse verte. La couleur et la forme ne dit qu'il est interne.)

Vous pouvez utiliser l' id() fonction intégrée d'apprendre ce que la valeur de référence (c'est l'adresse de l'objet cible).

Dans les langages compilés, une variable est un espace de mémoire qui est capable de capturer la valeur du type. En Python, une variable est un nom (capturé en interne comme une chaîne de caractères) lié à la référence de la variable qui contient la valeur de référence vers l'objet cible. Le nom de la variable est la clé dans le dictionnaire interne, la valeur de la partie de l'article de dictionnaire des magasins de la valeur de référence de la cible.

Les valeurs de référence sont cachés dans Python. Il n'y a pas de manière explicite le type d'utilisateur pour le stockage de la valeur de référence. Toutefois, vous pouvez utiliser un élément de la liste (ou d'un élément dans tout autre type de conteneur) comme variable de référence, parce que tous les conteneurs ne stocker les éléments que les références à des objets cibles. En d'autres termes, les éléments ne sont pas réellement contenue à l'intérieur du conteneur -- uniquement les références aux éléments.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X