Comment passer une variable par référence?

la documentation Python ne semble pas claire quant à savoir si les paramètres sont passés par référence ou valeur, et le code suivant produit 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'

y a-t-il quelque chose que je puisse faire pour passer la variable par référence réelle?

2110
demandé sur Taryn 2009-06-12 14:23:51
la source

23 ответов

Arguments adoptée par l'affectation . La raison en est double:

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

:

  • si vous passez un mutable objet dans une méthode, la méthode obtient une référence à ce même objet et vous pouvez le muter à la joie de votre cœur, mais si vous réinitialisez la référence dans la méthode, la portée extérieure ne saura rien à ce sujet, et après que vous avez fait, la référence extérieure pointera toujours à l'objet original.

  • si vous passez un immuable objet à une méthode, vous ne pouvez toujours pas rebind la référence extérieure, et vous il ne peut même pas muter l'objet.

pour être encore plus clair, donnons quelques exemples.

Liste - une mutable type

essayons de modifier la liste qui a été passée à 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 passé est une référence à outer_list , pas une copie de celui-ci, nous pouvons utiliser le la mutation de la liste des méthodes pour changer et avoir des changements indiqués dans l'extérieur de la portée.

voyons maintenant ce qui se passe lorsque nous essayons de changer la référence qui a été passée 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']

puisque le paramètre the_list a été passé par la valeur, lui attribuer 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 référence outer_list , et nous avions the_list pointer vers une nouvelle liste, mais il n'y avait aucun moyen de changer où outer_list pointait.

String-an immuable type

c'est immuable, donc il n'y a rien que nous puissions 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, puisque le paramètre the_string a été passé par la valeur, lui assignant une nouvelle chaîne n'a eu aucun effet que le code en dehors de la méthode pourrait voir. Le the_string était une copie de la référence outer_string , et nous avions the_string pointer vers une nouvelle chaîne, mais il n'y avait aucun moyen de changer où outer_string pointait.

j'espère que ça éclaircira un peu les choses.

EDIT: Il a été noté que cela ne répond pas à la question que @David avait initialement posée, "y a-t-il quelque chose que je puisse faire pour passer la variable par référence réelle?". Nous allons travailler là-dessus.

comment contourner cela?

comme le montre la réponse de @Andrea, vous pourriez retourner la nouvelle valeur. Cela ne change pas la façon dont les choses sont transmises, mais vous permet d'obtenir l'information que vous voulez retirer:

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 évitez d'utiliser une valeur de retour, vous pourriez créer une classe pour tenir 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 encombrant.

2279
répondu Blair Conrad 2017-04-03 05:13:38
la source

le problème vient d'une mauvaise compréhension de ce que les variables sont en Python. Si vous êtes habitué à la plupart des langues traditionnelles, vous avez un modèle mental de ce qui se passe dans la séquence suivante:

a = 1
a = 2

vous croyez que 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 comme ça que ça marche en Python. Plutôt, a commence comme une référence à un objet avec la valeur 1 , est alors réassigné comme référence à un objet avec la valeur 2 . Ces deux objets peuvent continuer à coexister même si a ne fait plus référence au premier; en fait, ils peuvent être partagés par un certain nombre d'autres références dans le 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 séparé de la référence qui a été utilisée dans l'appel de fonction, donc il n'y a aucun moyen de mettre à jour cette référence et de la faire renvoyer à un nouvel objet. Dans votre exemple:

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

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

self.variable est une référence à l'objet 'Original' . Lorsque vous appelez Change vous créez une deuxième référence var à l'objet. À l'intérieur de la fonction , vous réassignez la référence var à un autre objet de chaîne de caractères 'Changed' , mais la référence self.variable est séparée et ne change pas.

le seul moyen de contourner cela est de faire passer un objet mutable. Comme les deux références se rapportent au même objet, tout changement apporté à cet objet se reflète dans les deux endroits.

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'
546
répondu Mark Ransom 2017-04-03 03:39:27
la source

j'ai trouvé les autres réponses plutôt longues et compliquées, donc j'ai créé ce diagramme simple pour expliquer la façon dont Python traite les variables et les paramètres. enter image description here

253
répondu Zenadix 2016-05-25 18:30:05
la source

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

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

voici une citation significative:

"...les variables [noms] sont et non objets; elles ne peuvent pas être dénotées par d'autres variables ou référencées par des objets."

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

221
répondu David Cournapeau 2013-01-26 13:06:43
la source

pense à des choses passées par cession au lieu de 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 une affectation normale.

ainsi, en passant une liste à une fonction/méthode, la liste est assignée au nom du paramètre. L'ajout à la liste entraînera la modification de la liste. Reassignant la liste à l'intérieur de la fonction ne changera pas liste originale, depuis:

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

puisque les types immuables ne peuvent pas être modifiés, ils semblent comme étant passés par valeur - passer un int dans une fonction signifie attribuer l'int au paramètre fonctions. Vous pouvez seulement réattribuer cela, mais cela ne changera pas la valeur des variables originelles.

145
répondu Daren Thomas 2009-06-12 16:17:48
la source

Effbot (alias Fredrik Lundh) a décrit le style de passage variable de Python comme appel-par-objet: http://effbot.org/zone/call-by-object.htm

les objets sont attribués sur le tas et les pointeurs vers eux peuvent être passés n'importe où.

  • lorsque vous faites une affectation telle que x = 1000 , une entrée du dictionnaire est créée qui fait correspondre la chaîne de caractères "x" dans l'espace de nom courant à un pointeur vers le objet entier contenant mille.

  • lorsque vous mettez à jour "x" avec x = 2000 , un nouvel objet entier est créé et le dictionnaire est mis à jour pour pointer vers le nouvel objet. Le vieux mille objet est inchangé (et peut ou ne peut pas être vivant selon si quelque chose d'autre se réfère à l'objet).

  • lorsque vous faites une nouvelle affectation telle que y = x , une nouvelle entrée du dictionnaire "y" est créé qui pointe vers le même objet que l'entrée "x".

  • les objets comme les cordes et les entiers sont immuable . Cela signifie simplement qu'il n'existe pas de méthodes qui permettent de modifier l'objet après qu'il a été créé. Par exemple, une fois que l'objet integer mille est créé, il ne changera jamais. Les maths sont faites en créant de nouveaux objets integer.

  • objets comme des listes sont mutable . Cela signifie que le contenu de l'objet peut être modifié par tout ce qui pointe vers l'objet. Par exemple, x = []; y = x; x.append(10); print y affichera [10] . La liste vide est créé. Les deux" x "et" y " pointent vers la même Liste. La méthode ajoute mute (met à jour) l'objet list (comme Ajouter un enregistrement à une base de données) et le résultat est visible à la fois par "x" et par "y" (tout comme une mise à jour de base de données serait visible à chaque connexion à ce la base de données).

espoir qui clarifie la question pour vous.

54
répondu Raymond Hettinger 2013-03-29 08:41:44
la source

techniquement, Python utilise toujours des valeurs de référence . Je vais répéter mon autre réponse pour appuyer ma déclaration.

Python utilise toujours des valeurs pass-by-reference. Il n'y a pas d'exception. Toute assignation de variable signifie copier 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 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 sauter sur 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 a été passé par la valeur, l'extérieur lst ne pouvait pas être modifié. 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. La flèche bleue pleine est la valeur de référence qui a été transmise à la fonction (via le chemin de la flèche bleue tiretée). Le vilain jaune foncé est le dictionnaire interne. (Il pourrait en fait être dessiné aussi comme une ellipse verte. La couleur et la forme indiquent seulement qu'elle est interne.)

Vous pouvez utiliser le bouton 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 langues compilées, une variable est un espace mémoire capable de capturer la valeur du type. En Python, une variable est un nom (capturé en interne comme une chaîne) lié à la variable de référence qui détient la valeur de référence de l'objet cible. Le nom de la variable est la clé à l'intérieur Dictionnaire, la partie de valeur de cet article de dictionnaire stocke la valeur de référence à la cible.

Les valeurs de référence

sont cachées dans Python. Il n'y a pas de type d'utilisateur explicite pour stocker la valeur de référence. Cependant, vous pouvez utiliser un élément de liste (ou un élément dans n'importe quel autre type de conteneur approprié) comme variable de référence, parce que tous les conteneurs stockent les éléments aussi comme références aux objets cibles. En d'autres termes, les éléments ne sont pas contenus à l'intérieur du conteneur -- uniquement les références aux éléments.

53
répondu pepr 2017-05-23 15:18:21
la source

une astuce simple que j'utilise normalement est de simplement l'envelopper dans une liste:

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

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

(Oui je sais que cela peut être gênant, mais parfois c'est assez simple pour le faire.)

39
répondu AmanicA 2011-08-08 14:39:48
la source

(edit - Blair a mis à jour sa réponse extrêmement populaire afin qu'elle soit maintenant exacte)

je pense qu'il est important de noter que le post actuel avec le plus de voix (par Blair Conrad), tout en étant correcte en ce qui concerne son résultat, est trompeuse et est à la limite incorrecte sur la base de ses définitions. Bien qu'il existe de nombreux langages (comme C) qui permettent à l'utilisateur de passer soit par référence, soit par valeur, Python n'en est pas un.

La réponse de David Cournapeau renvoie à la vraie réponse et explique pourquoi le comportement dans le billet de Blair Conrad semble être correct alors que les définitions ne le sont pas.

dans la mesure où Python est pass by value, toutes les langues sont pass by value car un morceau de données (que ce soit une" valeur "ou une" référence") doit être envoyé. Cependant, cela ne signifie pas que Python est passé par la valeur dans le sens où un programmeur C y penserait.

si vous voulez le comportement, La réponse de Blair Conrad est très bien. Mais si vous voulez savoir pourquoi Python n'est ni pass by value ni pass by reference, lisez la réponse de David Cournapeau.

34
répondu KobeJohn 2015-10-09 18:00:42
la source

il n'y a pas de variables en Python

la clé pour comprendre la transmission de paramètres est d'arrêter de penser aux"variables". Il y a des noms et des objets en Python et ensemble ils apparaissent comme des variables, mais il est toujours utile de distinguer les trois.

  1. Python a des noms et des objets.
  2. se lie d'Affectation d'un nom à un objet.
  3. passer un argument dans une fonction aussi lie un nom (le nom du paramètre de la fonction) à un objet.

C'est tout ce qu'il y a. La mutabilité n'est pas pertinente pour cette question.

exemple:

a = 1

cela lie le nom a à un objet de type entier qui détient la valeur 1.

b = x

cela lie le nom b au même objet que le nom x est actuellement lié de. Par la suite, le nom b n'a plus rien à voir avec le nom x .

Voir les sections 3.1 et 4.2 dans le Python 3 de la langue de référence.


ainsi, dans le code indiqué dans la question, l'énoncé self.Change(self.variable) lie le nom var (dans le champ d'application de la fonction Change ) à l'objet qui détient la valeur 'Original' et le l'affectation var = 'Changed' (dans le corps de la fonction Change ) attribue à nouveau le même nom: à un autre objet (qui tient aussi une chaîne de caractères mais qui aurait pu être autre chose).

26
répondu Lutz Prechelt 2016-11-18 11:56:54
la source

vous avez de très bonnes réponses ici.

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )
22
répondu bobobobo 2012-01-27 08:28:48
la source

dans ce cas , la variable intitulée var dans la méthode Change est affectée d'une référence à self.variable , et vous assignez immédiatement une chaîne de caractères à var . Il ne pointe plus sur self.variable . L'extrait de code suivant montre ce qui se passerait si vous modifiez la structure de données pointée par var et self.variable , dans ce cas une liste:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

je suis sûr que quelqu'un d'autre pourrait clarifier cela davantage.

16
répondu Mike Mazur 2009-06-12 14:39:59
la source

le schéma pass-by-assignment de Python n'est pas tout à fait le même que L'option de paramètres de référence de C++, mais il s'avère être très similaire au modèle de passage d'argument du langage C (et d'autres) dans la pratique:

  • les arguments immuables sont effectivement passés " par la valeur ."Les objets tels que les entiers et les chaînes sont passés par référence d'objet au lieu de copier, mais parce que vous ne pouvez pas changer les objets immuables en place de toute façon, le l'effet est un peu comme faire une copie.
  • les arguments mutables sont effectivement passés " par le pointeur ."Des objets tels que des listes et les dictionnaires sont également passés par référence d'objet, qui est similaire à la façon dont C passe tableaux-pointeurs mutable objets peuvent être modifiés en place dans la fonction, un peu comme les tableaux C.
15
répondu ajkn1992 2015-03-27 07:38:18
la source

Comme vous pouvez l'etat, vous devez avoir un objet mutable, mais laissez-moi vous suggérons de vérifier les variables globales, car ils peuvent vous aider ou même de résoudre ce genre de problème!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

exemple:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2
13
répondu Nuno Aniceto 2016-08-09 05:23:54
la source

beaucoup d'idées dans les réponses ici, mais je pense qu'un point supplémentaire n'est pas clairement mentionné ici explicitement. Citation de la documentation python https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

" en Python, les variables qui ne sont référencées qu'à l'intérieur d'une fonction sont implicitement globales. Si une variable est assignée une nouvelle valeur n'importe où dans le corps de la fonction, il est supposé un local. Si une variable se voit attribuer une nouvelle valeur à l'intérieur de la fonction, la variable est implicitement locale, et vous devez la déclarer explicitement comme ‘globale’. Bien qu'un peu surprenant au début, la considération d'un moment explique ceci. D'une part, le fait d'exiger des variables globales pour les variables attribuées fournit une barre contre les effets secondaires involontaires. D'un autre côté, si global était requis pour toutes les références globales, vous utiliseriez global tout le temps. Vous devez déclarer comme mondiales chaque référence à un fonction intégrée ou à un composant d'un module importé. Ce désordre à l'encontre de l'utilité de la déclaration mondiale pour l'identification des effets secondaires."

même en passant un objet mutable à une fonction, ceci s'applique toujours. Et m'explique clairement la raison de la différence de comportement entre l'affectation de l'objet et de l'exploitation sur l'objet dans la fonction.

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

donne:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

le l'affectation à une variable globale qui n'est pas déclaré mondiale crée donc un nouvel objet local et rompt le lien à l'objet original.

12
répondu Joop 2014-09-29 11:12:10
la source

Voici l'explication simple (j'espère) du concept pass by object utilisé en Python.

Chaque fois que vous passez un objet à la fonction, l'objet lui-même est passé (l'objet en Python est en fait ce que vous appelleriez une valeur dans d'autres langages de programmation) pas la référence à cet objet. En d'autres termes, lorsque vous appelez:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

l'objet actuel - [0, 1] (qui serait appelé une valeur dans d'autres langages de programmation) est passé. Donc, en fait, la fonction change_me va essayer de faire quelque chose comme:

[0, 1] = [1, 2, 3]

qui évidemment ne changera pas l'objet passé à la fonction. Si la fonction ressemblait à ceci:

def change_me(list):
   list.append(2)

alors l'appel aboutirait à:

[0, 1].append(2)

qui va évidemment changer l'objet. Cette réponse , l'explique bien.

8
répondu matino 2017-05-23 14:54:59
la source

mis à part toutes les grandes explications sur la façon dont ce truc fonctionne en Python, Je ne vois pas de suggestion simple pour le problème. Comme vous semblez créer des objets et des instances, la façon pythonique de gérer les variables d'instance et de les Changer est la suivante:

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

    def Change(self):
        self.variable = 'Changed'

dans les méthodes d'instance, vous vous référez normalement à self pour accéder aux attributs d'instance. Il est normal pour définir les attributs d'instance dans __init__ et de les lire ou les modifier dans l'instance méthode. C'est aussi pourquoi vous passez self als le premier argument à def Change .

une Autre solution serait de créer une méthode statique comme ceci:

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

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var
7
répondu Dolf Andringa 2016-08-09 05:23:27
la source

il y a un petit truc pour passer un objet par référence, même si le langage ne le rend pas possible. Il fonctionne aussi en Java, c'est la liste avec un élément. ;- )

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

c'est un vilain piratage, mais ça marche. ;- P

6
répondu itmuckel 2016-04-21 19:47:42
la source

j'ai utilisé la méthode suivante pour convertir rapidement quelques codes Fortran en Python. Il est vrai que ce n'est pas par référence que la question originale a été posée, mais c'est un travail simple dans certains cas.

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c
4
répondu Brad Porter 2016-08-09 05:22:25
la source

alors que pass by reference n'est rien qui s'intègre bien dans python et devrait être rarement utilisé, il y a quelques solutions de rechange qui peuvent réellement fonctionner pour obtenir l'objet actuellement assigné à une variable locale ou même réattribuer une variable locale de l'intérieur d'une fonction appelée.

l'idée de base est d'avoir une fonction qui peut faire cet accès et peut être passée comme objet dans d'autres fonctions ou stockée dans une classe.

Une façon est d'utiliser des global (pour les variables globales) ou nonlocal (pour les variables locales à une fonction) dans une fonction wrapper.

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

la même idée fonctionne pour la lecture et del eting une variable.

pour la simple lecture, il y a même une façon plus courte de simplement utiliser lambda: x qui renvoie un callable qui, lorsqu'il est appelé, renvoie la valeur courante de X. C'est un peu comme "call by name" utilisé dans les langues du passé lointain.

Passer 3 wrappers pour accéder à une variable est un peu lourd donc ceux-ci peuvent être enveloppés dans une classe qui a un attribut proxy:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

pythons "reflection" support permet d'obtenir un objet capable de réassigner un nom/variable dans une portée donnée sans définir explicitement les fonctions dans cette portée:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

ici, la classe ByRef renferme un accès au dictionnaire. Donc l'accès à l'attribut wrapped est traduit à un accès d'article dans le dictionnaire passé. En passant le résultat du builtin locals et le nom d'une variable locale cela finit par Accéder à une variable locale. La documentation python à partir de 3.5 indique que changer le dictionnaire pourrait ne pas fonctionner mais il semble fonctionner pour moi.

3
répondu textshell 2016-08-20 17:02:37
la source

étant donné la façon dont python gère les valeurs et les références à celles-ci, la seule façon de référencer un attribut d'instance arbitraire est par son nom:

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

en code réel, vous ajouteriez, bien sûr, la vérification d'erreur sur la recherche de dict.

3
répondu mARK bLOORE 2016-10-31 18:33:23
la source

comme votre exemple est orienté objet, vous pouvez effectuer le changement suivant pour obtenir un résultat similaire:

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

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'
2
répondu Jesse Hogan 2017-09-10 05:19:53
la source

vous pouvez simplement utiliser une classe vide comme une instance pour stocker des objets de référence parce que les attributs d'un objet interne sont stockés dans un dictionnaire d'instance. Voir l'exemple.

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)
0
répondu sergzach 2018-05-03 17:14:11
la source