Question

La documentation Python semble pas clairement 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 at-il quelque chose que je peux faire pour passer la variable par référence réelle?

Était-ce utile?

La solution

Les arguments sont adoptée par la cession . La raison est double:

  1. le paramètre transmis est en fait un référence à un objet (mais la référence est passé par valeur)
  2. certains types de données sont mutables, mais d'autres ne 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 muter pour le plus grand plaisir de votre coeur, mais si vous REBIND la référence dans la méthode, la portée externe ne saura rien, et après que vous avez terminé, la référence externe pointeront toujours à l'objet original.

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

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

Liste - un type mutable

Essayons de modifier la liste qui a été passé à 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, pas une copie de celui-ci, nous pouvons utiliser les méthodes de liste mutationniste pour le changer et que les modifications reflétées dans le champ extérieur.

Maintenant, nous allons voir ce qui se passe quand nous essayons de changer la référence qui a été adoptée 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 paramètre the_list a été adopté par la valeur, l'attribution d'une nouvelle liste, il n'a eu aucun effet que le code en dehors de la méthode pourrait voir. Le the_list était une copie de la référence outer_list, et nous avons eu point de the_list à une nouvelle liste, mais il n'y avait aucun moyen de changer l'endroit où outer_list a.

String - un type immuable

Il est immuable, donc il n'y a rien que nous puissions faire pour changer le contenu de la chaîne

Maintenant, nous allons essayer 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, étant donné que le paramètre the_string a été adoptée par la valeur, l'attribution d'une nouvelle chaîne à elle 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 avons eu point de the_string à une nouvelle chaîne, mais il n'y avait aucun moyen de changer l'endroit où outer_string a.

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

EDIT: Il a été noté que cela ne répond pas à la question qui a demandé initialement @ David: « Est-il quelque chose que je peux faire pour passer la variable par référence réelle? ». Travaillons à ce sujet.

Comment peut-on contourner cela?

Comme @ spectacles de réponse d'Andrea, vous pouvez retourner la nouvelle valeur. Cela ne change pas la façon dont les choses sont passées, mais ne vous permet d'obtenir les informations que vous souhaitez revenir sur:

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 tenir votre valeur et de le transmettre 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.

Autres conseils

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 l'ordre suivant:

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. Cela ne veut pas comment les choses fonctionnent en Python. Au contraire, a commence comme une référence à un objet avec la 1 de valeur, 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 se réfère pas à la première plus; en fait, ils peuvent être partagées 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éée qui fait référence à l'objet passé. C'est distincte de la référence qui a été utilisé dans l'appel de fonction, donc il n'y a aucun moyen de mettre à jour cette référence et faire reportez-vous à 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 chaîne 'Original'. Lorsque vous appelez Change vous créez une deuxième var de référence à l'objet. A l'intérieur de la fonction réassignez var de référence à un 'Changed' objet chaîne différente, mais le self.variable de référence est distincte et ne change pas.

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

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

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

J'ai trouvé les autres réponses assez long et compliqué, donc je créé ce schéma simple pour expliquer la façon dont Python traite des variables et des paramètres. entrer image description ici «  loading=

Il est ni passer par valeur ou passage par référence - il est appel par objet. Voir ce, par Fredrik Lundh:

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

Voici une citation importante:

  

"... les variables [noms] sont pas objets;. Ils ne peuvent pas être désignés par d'autres variables ou visées par des objets"

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

Pensez à des choses étant passé par cession au lieu de par référence / par valeur. De cette façon, il est clair AllWays, ce qui se passe aussi longtemps que vous comprenez ce qui se passe lors de l'affectation normale.

Ainsi, lors du passage d'une liste à une fonction / méthode, la liste est affectée au nom du paramètre. À la liste Ajout entraînera la liste en cours de modification. Réaffectation la liste dans la fonction ne changera pas la liste initiale, puisque:

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é par valeur - en passant un moyen int en fonction attribuant l'int au paramètre fonctions. Vous ne pouvez réattribuer jamais, mais cela ne changera pas la valeur des variables originial.

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

Les objets sont alloués sur le tas et des pointeurs vers les peuvent être passés autour de partout.

  • Lorsque vous effectuez une mission telle que x = 1000, une entrée de dictionnaire est créé qui associe la chaîne « x » dans l'espace de nommage à un pointeur vers l'objet entier contenant un millier.

  • 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 d'un millier objet est inchangé (et peut ou non être en vie selon que quoi que ce soit d'autre se réfère à l'objet).

  • Lorsque vous effectuez une nouvelle affectation, comme y = x, une nouvelle entrée de dictionnaire « y » est créé qui pointe vers le même objet que l'entrée pour « x ».

  • Les objets tels que les chaînes et les entiers sont immuable . Cela signifie simplement qu'il n'y a pas de méthodes qui peuvent changer l'objet après qu'il a été créé. Par exemple, une fois que l'objet entier un mille est créé, il ne changera jamais. Math se fait en créant de nouveaux objets entiers.

  • Les objets tels que les listes sont mutable . Cela signifie que le contenu de l'objet peut être modifié par quoi que ce soit pointant vers l'objet. Par exemple, x = []; y = x; x.append(10); print y imprimera [10]. La liste vide a été créé. Les deux « x » et le point « y » à la même liste. append mute méthode (mises à jour) l'objet de la liste (comme l'ajout d'un enregistrement à une base de données) et le résultat est visible à la fois « x » et « y » (comme une mise à jour de base de données serait visible chaque connexion à cette base de données).

L'espoir qui clarifie la question pour vous.

Techniquement, Python utilise toujours passer par des valeurs de référence . Je vais répéter mon autre réponse pour soutenir ma déclaration.

Python utilise toujours passer par référence des valeurs. Il n'y a aucune exception. Toute affectation de variable signifie copie la valeur de référence. Pas 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é lorsqu'il est utilisé. De cette façon, en collaboration 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:

exemple illustré de faire passer l'argument

Si l'argument est passé par valeur, la lst extérieure 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 - dessinée comme la flèche. La flèche solide bleu est la valeur de référence qui a été transmise à la fonction (par l'intermédiaire du trajet de flèche bleue en pointillés). Le laid jaune foncé est le dictionnaire interne. (Il pourrait en fait également être appelée comme ellipse verte. La couleur et la forme dit seulement elle est interne.)

Vous pouvez utiliser le id() fonction intégrée pour apprendre ce que la valeur de référence est (à savoir, 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é à l'intérieur sous forme de chaîne) lié à la variable de référence qui contient la valeur de référence pour l'objet cible. Le nom de la variable est la clé dans le dictionnaire interne, la partie de la valeur de cet élément de dictionnaire stocke la valeur de référence à la cible.

Les valeurs de référence sont cachés en Python. Il n'y a aucun type explicite de l'utilisateur pour stocker la valeur de référence. Cependant, vous pouvez utiliser un élément de la liste (ou un élément dans tout autre type de récipient approprié) comme variable de référence, parce que tous les conteneurs ne stockent les éléments aussi comme des références aux objets cibles. En d'autres termes, les éléments ne sont pas réellement contenu dans le récipient - seules les références à des éléments sont

.

Un truc simple que j'utilise normalement est d'envelopper juste 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 il est assez simple de le faire.)

Il n'y a pas de variables en Python

La clé de transmission des paramètres de compréhension est d'arrêter de penser à des « variables ». Il y a des noms et des objets en Python et ensemble, ils apparaissent comme des variables, mais il est utile de distinguer toujours les trois.

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

C'est tout ce qu'il ya à faire. Mutabilité est hors de propos pour cette question.

Exemple:

a = 1

lie le nom a à un objet de type entier qui contient la valeur 1.

b = x

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

Voir les sections 3.1 et 4,2 dans la référence du langage Python 3.

Comment lire l'exemple dans la question

Dans le code indiqué dans la question, la déclaration self.Change(self.variable) lie le nom var (dans le cadre de la fonction Change) à l'objet qui contient la valeur 'Original' et la var = 'Changed' d'affectation (dans le corps de la fonction Change) attribue le même nom nouveau:. à un autre objet (ce qui arrive à maintenir une chaîne aussi bien, mais aurait pu être tout autre chose)

Comment passer par référence

(modifier 28/04/2019)

Donc, si la chose que vous voulez changer est un objet mutable, il n'y a pas de problème, car tout est effectivement passé par référence.

Si c'est un objet immuable (par exemple un bool, numéro, chaîne), le chemin à parcourir est de l'envelopper dans un objet mutable.
La solution rapide et sale pour cela est une liste d'un élément (au lieu de self.variable, passer [self.variable] et dans la fonction modifier var[0]).
L'approche plus pythonique serait d'introduire un trivial, classe un attribut. La fonction reçoit une instance de la classe et manipule l'attribut.

(modifier - Blair a mis à jour sa réponse très populaire pour qu'il soit maintenant exact)

Je pense qu'il est important de noter que le poste actuel avec le plus de voix (par Blair Conrad), tout en étant correct par rapport à son résultat, est trompeur et est à la limite incorrecte en fonction de ses définitions. Bien qu'il existe de nombreuses langues (comme C) qui permettent à l'utilisateur de passer soit par référence ou passer par valeur, Python est pas un d'entre eux.

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

Dans la mesure où Python est passe par la valeur, toutes les langues sont passe par la valeur depuis 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 passe par la valeur dans le sens où un programmeur C serait penser.

Si vous voulez que le comportement, la réponse de Blair Conrad est très bien. Mais si vous voulez connaître les écrous et les boulons de Python est pourquoi ne passe par valeur ou passer par référence, lisez la réponse de David Cournapeau.

Vous avez des réponses vraiment bien 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' )

Dans ce cas, la variable intitulée var dans la méthode Change est attribuée une référence à self.variable, et vous assignez immédiatement une chaîne à var. Il n'y a pas de pointage plus à 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 davantage cette question.

régime passe par l'affectation de Python est pas tout à fait la même chose que l'option des paramètres de référence de C ++, mais il se révèle être très semblable au modèle passant argument du langage C (et d'autres) dans la pratique:

  • arguments Immuable 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 par la copie, mais parce que vous ne pouvez pas modifier les objets immuables en place de toute façon, la effet est un peu comme faire une copie.
  • arguments Mutable sont effectivement passés « par pointeur . » Des objets tels que des listes et les dictionnaires sont également transmis par référence à l'objet, qui est similaire à la façon C laissez-passer des tableaux comme des objets pointeurs-mutables peuvent être modifiés en place dans la fonction, un peu comme les tableaux C.

Comme vous pouvez le dire besoin d'avoir un objet mutable, mais laissez-moi vous suggère de vérifier sur les variables globales, car ils peuvent vous aider à résoudre ou même ce genre de question!

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

Beaucoup d'idées de réponses, mais je pense que d'un point supplémentaire n'est pas clairement mentionné explicitement ici. Je cite 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 dans une fonction sont globales. Implicitement Si une variable est affectée une nouvelle valeur partout dans le corps de la fonction, il est supposé être un local. Si une variable est toujours assignée une nouvelle valeur à l'intérieur la fonction, la variable est implicitement local, et vous devez déclarer explicitement comme « global ». Bien qu'un peu surprenant au premier abord, l'examen d'un moment explique cela. D'une part, ce qui nécessite global pour les variables affectées fournit une barre contre les effets secondaires indésirables. D'autre part, si global était nécessaire pour toutes les références globales, vous seriez en utilisant tout le temps global. Il faudrait déclarer mondiale toute référence à une fonction intégrée ou à un composant d'un module importé. Ce fouillis irait à l'encontre de l'utilité de la déclaration globale pour identifier les effets secondaires. "

Même si le passage d'un objet mutable à une fonction cela vaut encore. Et me explique clairement la raison de la différence de comportement entre l'affectation à l'objet et l'exploitation de 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

L'affectation à une variable globale qui n'a pas été déclarée mondiale crée donc un nouvel objet local et rompt le lien vers l'objet d'origine.

Voici simple (je l'espère) explication du concept pass by object utilisé en Python.
Chaque fois que vous passez un objet à la fonction, l'objet lui-même est passé (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 réel - [0, 1] (qui serait appelé une valeur dans d'autres langages de programmation) est transmis. 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)

Ensuite, l'appel entraînerait:

[0, 1].append(2)

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

En dehors de toutes les grandes explications sur la façon dont fonctionne ce genre de choses en Python, je ne vois pas une simple suggestion du problème. Comme vous semblez le faire créer des objets et des instances, la façon de traiter pythonique variables d'instance et les changer est le suivant:

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 faites référence normalement self pour accéder attributs d'instance. Il est normal de définir les attributs d'instance dans __init__ et lire ou les modifier dans les méthodes d'instance. C'est aussi la raison pour laquelle 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

Il y a un petit truc pour passer un objet par référence, même si la langue ne permet pas. Il fonctionne en Java aussi, il 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

Il est un bidouille horrible, mais cela fonctionne. ;-P

J'ai utilisé la méthode suivante pour convertir rapidement quelques codes Fortran à Python. Certes, il est passe pas par référence comme l'a posé la question initiale, mais est un travail simple autour 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

Alors que passe par référence est rien qui va bien en python et devrait être rarement utilisé il y a quelques solutions de contournement qui peut effectivement travailler pour obtenir l'objet actuellement affecté à une variable locale ou même réassigner une variable locale à l'intérieur d'une fonction appelée.

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

La première consiste à utiliser global (pour les variables globales) ou nonlocal (pour les variables locales dans une fonction) dans une fonction enveloppe.

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 deleting une variable.

Pour la simple lecture il y a même un chemin plus court d'utiliser simplement lambda: x qui retourne une appelable que lorsque appelé renvoie la valeur actuelle de x. Ceci est un peu comme « appel par le nom » utilisé dans les langues dans le passé lointain.

Passe 3 wrappers pour accéder à une variable est un peu difficile à manier de sorte que ceux peut être enveloppé 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

soutien Pythons « réflexion » permet d'obtenir un objet qui est capable de réattribuer un nom / variable dans un champ donné sans définir explicitement les fonctions dans ce champ:

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 enveloppe un accès au dictionnaire. attribut Donc, l'accès à wrapped est traduit à un accès d'élément dans le dictionnaire passé. En passant le résultat de la locals et le nom builtin d'une variable locale finit par accéder à cette variable locale. La documentation de Python de 3.5 signale que la modification du dictionnaire pourrait ne pas fonctionner, mais il semble fonctionner pour moi.

étant donné le python gère ainsi les valeurs et les références à eux, la seule façon que vous pouvez référencer un attribut d'instance arbitraire est par nom:

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

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

dans votre code vous, bien sûr, ajouter le contrôle d'erreur sur la recherche dict.

Pass-By-Reference en Python est tout à fait différent du concept de passage par référence en C ++ / Java.

  • Java & C #: les types primitifs (comprennent string) passe par la valeur (copie), le type de référence est passé par référence (adresse copie) afin que tous les changements apportés au paramètre dans la fonction appelée sont visibles l'appelant.
  • C ++: Les deux passent par référence ou passage par valeur sont autorisés. Si un paramètre est passé par référence, vous pouvez le modifier ou non selon que le paramètre a été passé comme const ou non. Cependant, const ou non, le paramètre maintient la référence à l'objet et référence ne peut pas être attribué à pointer vers un objet différent au sein de la fonction appelée.
  • Python: Python est « référence passe par objet », dont on dit souvent: «objet références sont passés par valeur » [Lire ici] 1 . Les deux l'appelant et la fonction se réfèrent au même objet, mais le paramètre dans la fonction est une nouvelle variable qui est juste une copie tient de l'objet dans l'appelant. Comme C ++, un paramètre peut être modifié ou non en fonction - Cela dépend du type d'objet passé. par exemple; Un type d'objet immuable ne peut être modifié dans la fonction appelée alors qu'un objet mutable peut être soit mis à jour ou réinitialisée. Une différence fondamentale entre la mise à jour ou de ré-affectation / ré-initialisation de la variable mutable est que la valeur mise à jour se réfléchit avant la fonction appelée alors que la valeur réinitialisée ne fonctionne pas. Champ d'application de toute cession de nouvel objet à une variable mutable est locale à la fonction dans le python. Les exemples fournis par @ blair-conrad sont grands pour comprendre.

Étant donné que votre exemple se trouve être orienté objet, vous pouvez faire 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'

Comme il semble être mentionné nulle part une approche pour simuler des références comme par exemple connu par C ++ est d'utiliser une fonction « mise à jour » et de transmettre, au lieu de la variable réelle (ou plutôt, « name »):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

Il est surtout utile pour « out-seules références » ou dans une situation avec plusieurs threads / processus (en faisant le fil / multitraitement fonction de mise à jour de sécurité).

Il est évident que le ne permet pas au-dessus de lire la valeur, la mise à jour seulement elle.

Vous pouvez simplement utiliser une classe vide comme une instance pour stocker des objets de référence, car l'objet en interne les attributs 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)

Depuis les dictionnaires sont passés par référence, vous pouvez utiliser une variable dict pour stocker toutes les valeurs référencées à l'intérieur.

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top