¿Cómo paso una variable por referencia?

La documentación de Python parece poco clara sobre si los parámetros se pasan por referencia o por valor, y el siguiente código produce el valor sin cambios ‘Original’

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

¿Hay algo que pueda hacer para pasar la variable por referencia real?

Los argumentos son pasados ​​por asignación . La razón detrás de esto es doble:

  1. el parámetro pasado es en realidad una referencia a un objeto (pero la referencia se pasa por valor)
  2. algunos tipos de datos son mutables, pero otros no lo son

Asi que:

  • Si pasa un objeto mutable a un método, el método obtiene una referencia a ese mismo objeto y puede mutarlo para deleite de su corazón, pero si vuelve a enlazar la referencia en el método, el ámbito externo no sabrá nada al respecto, y luego Ya ha terminado, la referencia externa todavía apuntará al objeto original.

  • Si pasa un objeto inmutable a un método, aún no puede volver a enlazar la referencia externa y ni siquiera puede mutar el objeto.

Para que sea aún más claro, vamos a tener algunos ejemplos.

Lista – un tipo mutable

Intentemos modificar la lista que se pasó a un método:

 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) 

Salida:

 before, outer_list = ['one', 'two', 'three'] got ['one', 'two', 'three'] changed to ['one', 'two', 'three', 'four'] after, outer_list = ['one', 'two', 'three', 'four'] 

Dado que el parámetro pasado es una referencia a la outer_list , no una copia, podemos usar los métodos de la lista de mutación para cambiarlo y que los cambios se reflejen en el ámbito externo.

Ahora veamos qué sucede cuando intentamos cambiar la referencia que se pasó como parámetro:

 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) 

Salida:

 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'] 

Dado que el parámetro the_list se pasó por valor, asignarle una nueva lista no tuvo ningún efecto que el código fuera del método pudiera ver. the_list era una copia de la referencia de outer_list , y teníamos el punto the_list apuntando a una nueva lista, pero no había forma de cambiar a dónde señalaba la lista outer_list .

Cadena – un tipo inmutable

Es inmutable, así que no hay nada que podamos hacer para cambiar el contenido de la cadena.

Ahora, intentemos cambiar la referencia

 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) 

Salida:

 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 

Nuevamente, dado que el parámetro the_string se pasó por valor, la asignación de una nueva cadena no tuvo ningún efecto que el código fuera del método pudiera ver. the_string era una copia de la referencia de outer_string , y teníamos el punto de cadena a una nueva cadena, pero no había forma de cambiar a dónde señalaba la outer_string .

Espero que esto aclare un poco las cosas.

EDITAR: Se ha observado que esto no responde a la pregunta que @David originalmente formuló: “¿Hay algo que pueda hacer para pasar la variable por referencia real?”. Vamos a trabajar en eso.

¿Cómo podemos solucionar esto?

Como muestra la respuesta de @ Andrea, podría devolver el nuevo valor. Esto no cambia la forma en que se transmiten las cosas, pero le permite volver a obtener la información que desea:

 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 realmente desea evitar el uso de un valor de retorno, puede crear una clase para mantener su valor y pasarlo a la función o usar una clase existente, como una lista:

 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]) 

Aunque esto parece un poco engorroso.

El problema proviene de un malentendido de qué variables están en Python. Si estás acostumbrado a la mayoría de los lenguajes tradicionales, tienes un modelo mental de lo que sucede en la siguiente secuencia:

 a = 1 a = 2 

Usted cree que a es una ubicación de memoria que almacena el valor 1 , luego se actualiza para almacenar el valor 2 . Así no es como funcionan las cosas en Python. Más bien, a comienza como una referencia a un objeto con el valor 1 , luego se reasigna como una referencia a un objeto con el valor 2 . Esos dos objetos pueden continuar coexistiendo aunque ya no se refiera al primero; de hecho, pueden ser compartidos por cualquier número de otras referencias dentro del progtwig.

Cuando llama a una función con un parámetro, se crea una nueva referencia que se refiere al objeto pasado. Esto es independiente de la referencia que se usó en la llamada a la función, por lo que no hay manera de actualizar esa referencia y hacer que se refiera a un nuevo objeto En tu ejemplo:

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

self.variable es una referencia al objeto de cadena 'Original' . Cuando llama a Change , crea una segunda referencia var al objeto. Dentro de la función, reasigna la var referencia a un objeto de cadena diferente 'Changed' , pero la referencia self.variable está separada y no cambia.

La única forma de evitar esto es pasar un objeto mutable. Debido a que ambas referencias se refieren al mismo objeto, cualquier cambio en el objeto se refleja en ambos lugares.

 def __init__(self): self.variable = ['Original'] self.Change(self.variable) def Change(self, var): var[0] = 'Changed' 

Encontré las otras respuestas bastante largas y complicadas, así que creé este diagtwig simple para explicar la forma en que Python trata las variables y los parámetros. introduzca la descripción de la imagen aquí

No es ni paso por valor ni paso por referencia, es llamada por objeto. Mira esto, por Fredrik Lundh:

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

Aquí hay una cita significativa:

“… las variables [nombres] no son objetos; no pueden ser denotadas por otras variables ni referidas por objetos”.

En su ejemplo, cuando se llama al método Change , se crea un espacio de nombres para él; y var convierte en un nombre, dentro de ese espacio de nombres, para el objeto de cadena 'Original' . Ese objeto entonces tiene un nombre en dos espacios de nombres. A continuación, var = 'Changed' une var a un nuevo objeto de cadena, y por lo tanto, el espacio de nombres del método se olvida de 'Original' . Finalmente, ese espacio de nombres se olvida, y la cadena 'Changed' junto con él.

Piense en cosas que se pasan por asignación en lugar de por referencia / por valor. De esa manera, siempre está claro, lo que está sucediendo siempre y cuando se entienda lo que sucede durante la asignación normal.

Entonces, al pasar una lista a una función / método, la lista se asigna al nombre del parámetro. Anexar a la lista resultará en la modificación de la lista. La reasignación de la lista dentro de la función no cambiará la lista original, ya que:

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

Dado que los tipos inmutables no se pueden modificar, parece que se pasan por un valor: pasar un int a una función significa asignar el int al parámetro de funciones. Solo puedes reasignar eso, pero no cambiará el valor de las variables originales.

Effbot (también conocido como Fredrik Lundh) ha descrito el estilo de paso variable de Python como llamada por objeto: http://effbot.org/zone/call-by-object.htm

Los objetos se asignan en el montón y los punteros a ellos se pueden pasar a cualquier lugar.

  • Cuando realiza una asignación como x = 1000 , se crea una entrada de diccionario que asigna la cadena “x” en el espacio de nombres actual a un puntero al objeto entero que contiene mil.

  • Cuando actualiza “x” con x = 2000 , se crea un nuevo objeto entero y el diccionario se actualiza para que apunte al nuevo objeto. El antiguo mil objeto no se modifica (y puede o no estar vivo dependiendo de si alguna otra cosa se refiere al objeto).

  • Cuando realiza una nueva asignación como y = x , se crea una nueva entrada de diccionario “y” que apunta al mismo objeto que la entrada de “x”.

  • Objetos como cadenas y enteros son inmutables . Esto simplemente significa que no hay métodos que puedan cambiar el objeto después de que se haya creado. Por ejemplo, una vez que se crea el objeto entero mil, nunca cambiará. Matemáticas se realiza mediante la creación de nuevos objetos enteros.

  • Objetos como listas son mutables . Esto significa que el contenido del objeto se puede cambiar por cualquier cosa que apunte al objeto. Por ejemplo, x = []; y = x; x.append(10); print y x = []; y = x; x.append(10); print y x = []; y = x; x.append(10); print y se imprimirá [10] . Se creó la lista vacía. Tanto “x” como “y” apuntan a la misma lista. El método de anexión muta (actualiza) el objeto de la lista (como agregar un registro a una base de datos) y el resultado es visible tanto para “x” como para “y” (al igual que una actualización de la base de datos sería visible para cada conexión a esa base de datos).

Espero que te aclare el tema.

Técnicamente, Python siempre usa los valores de paso por referencia . Voy a repetir mi otra respuesta para apoyar mi statement.

Python siempre usa valores de paso por referencia. No hay ninguna excepción. Cualquier asignación de variable significa copiar el valor de referencia. Sin excepción. Cualquier variable es el nombre vinculado al valor de referencia. Siempre.

Puede pensar en un valor de referencia como la dirección del objeto de destino. La dirección se elimina automáticamente cuando se utiliza. De esta manera, al trabajar con el valor de referencia, parece que trabaja directamente con el objeto de destino. Pero siempre hay una referencia en el medio, un paso más para saltar al objective.

Aquí está el ejemplo que prueba que Python usa pasar por referencia:

Ejemplo ilustrado de pasar el argumento.

Si el argumento fue pasado por valor, el lst externo no pudo ser modificado. Los verdes son los objetos de destino (el negro es el valor almacenado en el interior, el rojo es el tipo de objeto), el amarillo es la memoria con el valor de referencia en el interior, dibujado como la flecha. La flecha azul sólida es el valor de referencia que se pasó a la función (a través de la ruta de la flecha azul discontinua). El feo amarillo oscuro es el diccionario interno. (De hecho, podría dibujarse también como una elipse verde. El color y la forma solo dicen que es interno).

Puede usar la función incorporada id() para saber cuál es el valor de referencia (es decir, la dirección del objeto de destino).

En los lenguajes comstackdos, una variable es un espacio de memoria que puede capturar el valor del tipo. En Python, una variable es un nombre (capturado internamente como una cadena) vinculado a la variable de referencia que contiene el valor de referencia para el objeto de destino. El nombre de la variable es la clave en el diccionario interno, la parte de valor de ese elemento del diccionario almacena el valor de referencia al objective.

Los valores de referencia están ocultos en Python. No hay ningún tipo de usuario explícito para almacenar el valor de referencia. Sin embargo, puede usar un elemento de lista (o elemento en cualquier otro tipo de contenedor adecuado) como variable de referencia, porque todos los contenedores almacenan los elementos también como referencias a los objetos de destino. En otras palabras, los elementos en realidad no están contenidos dentro del contenedor, solo lo están las referencias a los elementos.

Un truco simple que normalmente utilizo es simplemente envolverlo en una lista:

 def Change(self, var): var[0] = 'Changed' variable = ['Original'] self.Change(variable) print variable[0] 

(Sí, sé que esto puede ser un inconveniente, pero a veces es lo suficientemente simple para hacer esto).

(edición – Blair ha actualizado su respuesta enormemente popular para que ahora sea precisa)

Creo que es importante tener en cuenta que la publicación actual con la mayoría de los votos (por Blair Conrad), aunque es correcta con respecto a su resultado, es engañosa y está en el límite incorrecto según sus definiciones. Si bien hay muchos idiomas (como C) que permiten al usuario pasar por referencia o por valor, Python no es uno de ellos.

La respuesta de David Cournapeau apunta a la respuesta real y explica por qué el comportamiento en la publicación de Blair Conrad parece ser correcto, mientras que las definiciones no lo son.

En la medida en que Python se pasa por valor, todos los idiomas se pasan por valor ya que algunos datos (ya sea un “valor” o una “referencia”) deben enviarse. Sin embargo, eso no significa que Python se pase por valor en el sentido de que un progtwigdor de C lo pensaría.

Si quieres el comportamiento, la respuesta de Blair Conrad está bien. Pero si desea conocer los detalles de por qué Python no es pasar por valor ni pasar por referencia, lea la respuesta de David Cournapeau.

No hay variables en Python

La clave para entender el paso de parámetros es dejar de pensar en “variables”. Hay nombres y objetos en Python y juntos aparecen como variables, pero es útil distinguir siempre los tres.

  1. Python tiene nombres y objetos.
  2. La asignación enlaza un nombre a un objeto.
  3. Pasar un argumento a una función también vincula un nombre (el nombre del parámetro de la función) a un objeto.

Eso es todo lo que hay que hacer. La mutabilidad es irrelevante para esta pregunta.

Ejemplo:

 a = 1 

Esto une el nombre a a un objeto de tipo entero que contiene el valor 1.

 b = x 

Esto vincula el nombre b con el mismo objeto al que está vinculado actualmente el nombre x . Después, el nombre b ya no tiene nada que ver con el nombre x .

Consulte las secciones 3.1 y 4.2 en la referencia del lenguaje Python 3.


Por lo tanto, en el código que se muestra en la pregunta, la statement self.Change(self.variable) vincula el nombre var (en el scope de la función Change ) al objeto que contiene el valor 'Original' y la asignación var = 'Changed' ( en el cuerpo de la función Change ) asigna ese mismo nombre otra vez: a algún otro objeto (que también contiene una cadena, pero podría haber sido algo completamente distinto).

Tienes algunas respuestas realmente buenas aquí.

 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' ) 

En este caso, a la variable titulada var en el método Change se le asigna una referencia a self.variable , e inmediatamente asigna una cadena a var . Ya no está apuntando a self.variable . self.variable . El siguiente fragmento de código muestra lo que sucedería si modificara la estructura de datos apuntada por var y self.variable , en este caso una lista:

 >>> 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'] >>> 

Estoy seguro de que alguien más podría aclarar esto aún más.

El esquema de paso por asignación de Python no es exactamente lo mismo que la opción de parámetros de referencia de C ++, pero resulta ser muy similar al modelo de paso de argumentos del lenguaje C (y otros) en la práctica:

  • Los argumentos inmutables se pasan efectivamente ” por valor “. Los objetos como enteros y cadenas se pasan por referencia de objeto en lugar de copiarlos, pero como no se pueden cambiar los objetos inmutables en su lugar, el efecto es muy parecido a hacer una copia.
  • Los argumentos mutables se pasan efectivamente ” por puntero “. Los objetos, como listas y diccionarios, también se pasan por referencia a objetos, que es similar a la forma en que C pasa a las matrices como punteros: los objetos mutables pueden cambiarse en el lugar de la función, al igual que las matrices C .

Como puede indicar, necesita tener un objeto mutable, pero permítame sugerirle que compruebe las variables globales, ya que pueden ayudarlo o incluso a resolver este tipo de problema.

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

ejemplo:

 >>> def x(y): ... global z ... z = y ... >>> x  >>> y Traceback (most recent call last): File "", line 1, in  NameError: name 'y' is not defined >>> z Traceback (most recent call last): File "", line 1, in  NameError: name 'z' is not defined >>> x(2) >>> x  >>> y Traceback (most recent call last): File "", line 1, in  NameError: name 'y' is not defined >>> z 2 

A lot of insights in answers here, but i think an additional point is not clearly mentioned here explicitly. Quoting from python documentation https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’. Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.”

Even when passing a mutable object to a function this still applies. And to me clearly explains the reason for the difference in behavior between assigning to the object and operating on the object in the function.

 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) 

da:

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

The assignment to an global variable that is not declared global therefore creates a new local object and breaks the link to the original object.

Here is the simple (I hope) explanation of the concept pass by object used in Python.
Whenever you pass an object to the function, the object itself is passed (object in Python is actually what you’d call a value in other programming languages) not the reference to this object. In other words, when you call:

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

The actual object – [0, 1] (which would be called a value in other programming languages) is being passed. So in fact the function change_me will try to do something like:

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

which obviously will not change the object passed to the function. If the function looked like this:

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

Then the call would result in:

 [0, 1].append(2) 

which obviously will change the object. This answer explains it well.

Aside from all the great explanations on how this stuff works in Python, I don’t see a simple suggestion for the problem. As you seem to do create objects and instances, the pythonic way of handling instance variables and changing them is the following:

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

In instance methods, you normally refer to self to access instance attributes. It is normal to set instance attributes in __init__ and read or change them in instance methods. That is also why you pass self als the first argument to def Change .

Another solution would be to create a static method like this:

 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 

There is a little trick to pass an object by reference, even though the language doesn’t make it possible. It works in Java too, it’s the list with one item. 😉

 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 

It’s an ugly hack, but it works. ;-P

I used the following method to quickly convert a couple of Fortran codes to Python. True, it’s not pass by reference as the original question was posed, but is a simple work around in some cases.

 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 

While pass by reference is nothing that fits well into python and should be rarely used there are some workarounds that actually can work to get the object currently assigned to a local variable or even reassign a local variable from inside of a called function.

The basic idea is to have a function that can do that access and can be passed as object into other functions or stored in a class.

One way is to use global (for global variables) or nonlocal (for local variables in a function) in a wrapper function.

 def change(wrapper): wrapper(7) x = 5 def setter(val): global x x = val print(x) 

The same idea works for reading and del eting a variable.

For just reading there is even a shorter way of just using lambda: x which returns a callable that when called returns the current value of x. This is somewhat like “call by name” used in languages in the distant past.

Passing 3 wrappers to access a variable is a bit unwieldy so those can be wrapped into a class that has a proxy attribute:

 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 makes it possible to get a object that is capable of reassigning a name/variable in a given scope without defining functions explicitly in that scope:

 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) 

Here the ByRef class wraps a dictionary access. So attribute access to wrapped is translated to a item access in the passed dictionary. By passing the result of the builtin locals and the name of a local variable this ends up accessing a local variable. The python documentation as of 3.5 advises that changing the dictionary might not work but it seems to work for me.

given the way python handles values and references to them, the only way you can reference an arbitrary instance attribute is by name:

 class PassByReferenceIsh: def __init__(self): self.variable = 'Original' self.change('variable') print self.variable def change(self, var): self.__dict__[var] = 'Changed' 

in real code you would, of course, add error checking on the dict lookup.

Since your example happens to be object-oriented, you could make the following change to achieve a similar result:

 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' 

Pass-By-Reference in Python is quite different from the concept of pass by reference in C++/Java.

  • Java: Everything is passed by reference so all changes made in the parameter in the called function are visible to the caller.
  • C++: Both pass-by-reference or pass-by-value are allowed. If a parameter is passed by reference, you can either modify it or not depending upon whether the parameter was passed as const or not. However, const or not, the parameter maintains the reference to the object and reference cannot be assigned to point to a different object within the called function.
  • Python: Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.”[Read here] 1 . Both the caller and the function refer to the same object but the parameter in the function is a new variable which is just holding a copy of the object in the caller. Like C++, a parameter can be either modified or not in function – This depends upon the type of object passed. eg; An immutable object type cannot be modified in the called function whereas a mutable object can be either updated or re-initialized. A crucial difference between updating or re-assigning/re-initializing the mutable variable is that updated value gets reflected back in the called function whereas the reinitialized value does not. Scope of any assignment of new object to a mutable variable is local to the function in the python. Examples provided by @blair-conrad are great to understand this.

You can merely use an empty class as an instance to store reference objects because internally object attributes are stored in an instance dictionary. See the example.

 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)