Pregunta

La documentación de Python no parece clara acerca de 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?

¿Fue útil?

Solución

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

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

Así que:

  • Si pasa un mutable objeto en un método, el método obtiene una referencia a ese mismo objeto y que puede mutar para deleite de su corazón, pero si volver a enlazar la referencia en el método, el alcance exterior sabrá nada sobre él, y después de que haya terminado, la referencia externa todavía apuntará al objeto original.

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

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

Lista - un tipo mutable

Vamos a tratar de modificar la lista que se ha pasado 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 en es una referencia a outer_list, no una copia de la misma, podemos utilizar los métodos de la lista mutante para cambiarlo y tienen los cambios reflejados en el ámbito exterior.

Ahora vamos a ver lo que sucede cuando tratamos de cambiar la referencia que se pasa como un 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']

Desde el parámetro the_list se pasa por valor, la asignación de una nueva lista a que no tuvo efecto que el código fuera del método podía ver. El the_list era una copia de la referencia outer_list, y tuvimos punto the_list a una nueva lista, pero no había manera de cambiar el lugar donde outer_list señaló.

String - un tipo inmutable

Es inmutable, por lo que no podemos hacer nada para cambiar el contenido de la cadena

Ahora, vamos a tratar de 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

Una vez más, ya que el parámetro the_string se pasa por valor, la asignación de una nueva cadena para que no tuvo efecto que el código fuera del método podía ver. El the_string era una copia de la referencia outer_string, y tuvimos punto the_string a una nueva cadena, pero no había manera de cambiar el lugar donde outer_string señaló.

Espero que esto aclare un poco las cosas.

EDIT: Se ha observado que esto no responde a la pregunta que @ David pidió originalmente, "¿Hay algo que pueda hacer para pasar la variable de referencia real?". Vamos a trabajar en eso.

¿Cómo podemos evitar esto?

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

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 quería evitar el uso de un valor de retorno, se podría crear una clase para mantener su valor y pasarlo a la función o el uso de 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])

A pesar de que esto parece un poco engorroso.

Otros consejos

El problema viene de una mala interpretación de lo que son las variables en Python. Si estás acostumbrado a la mayoría de los lenguajes tradicionales, tiene un modelo mental de lo que ocurre en la siguiente secuencia:

a = 1
a = 2

Se cree que a es una ubicación de memoria que almacena el valor 1, a continuación, se actualiza para almacenar el valor 2. No es así como funcionan las cosas en Python. Más bien, a comienza como una referencia a un objeto con el valor 1, a continuación, se reasigna como una referencia a un objeto con el valor 2. Esos dos objetos pueden seguir coexistiendo a pesar de que a no se refiere a la primera ya; De hecho, pueden ser compartidos por cualquier número de otras referencias dentro del programa.

Cuando se llama a una función con un parámetro, se crea una nueva referencia que hace referencia al objeto pasado. Esto es independiente de la referencia que se utilizó en la llamada de función, así que no hay manera de actualizar esa referencia y hacerla referirse a un nuevo objeto. En su ejemplo:

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

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

self.variable es una referencia a la 'Original' objeto de cadena. Cuando se llama a Change crea una segunda var referencia al objeto. Dentro de la función reasigna el var referencia a un objeto de cadena 'Changed' diferente, pero el self.variable referencia es independiente 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 reflejan en ambos lugares.

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

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

Me pareció que las otras respuestas bastante largo y complicado, así que creé este sencillo diagrama para explicar la forma en que trata a Python variables y parámetros. introducir descripción de la imagen aquí

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

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

He aquí una cita significativa:

"...las variables [nombres] son no objetos;no pueden ser denotados por otras variables ni referenciados por objetos."

En su ejemplo, cuando el Change El método se llama--a espacio de nombres es creado para ello;y var se convierte en un nombre, dentro de ese espacio de nombres, para el objeto de cadena 'Original'.Ese objeto tiene entonces un nombre en dos espacios de nombres.Próximo, var = 'Changed' se une var a un nuevo objeto de cadena y, por lo tanto, el espacio de nombres del método se olvida 'Original'.Finalmente, ese espacio de nombres se olvida y la cadena 'Changed' junto con eso.

Piense en las cosas que se pasa por la asignación en lugar de por referencia / por valor. De esta manera, es Allways clara, lo que está sucediendo todo el tiempo que entiende lo que sucede durante la asignación normal.

Así, al pasar de una lista a una función / método, se asigna a la lista el nombre del parámetro. Al añadir a la lista resultará en la lista que se modifica. Reasignación de la lista dentro de , la función no va a 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']

Como los tipos inmutables no pueden ser modificados, que parecen como ser pasado por valor - pasar un int en una función significa asignar el int con el parámetro de funciones. Sólo se puede reasignar vez de eso, pero no va a cambiar el valor de las variables originial.

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

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

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

  • Al actualizar "x" con x = 2000, se crea un nuevo objeto entero y el diccionario se actualiza para señalar al nuevo objeto. La edad de mil objetos no se modifica (y puede o no estar vivo en función de si todo lo demás se refiere al objeto).

  • Al hacer una nueva asignación como y = x, se crea una nueva entrada de diccionario "y" que apunta al mismo objeto que la entrada para "x".

  • Los objetos como cuerdas y son números enteros inmutable . Esto simplemente significa que no existen métodos que pueden cambiar el objeto después de que se ha creado. Por ejemplo, una vez que el número entero objeto de mil se crea, nunca va a cambiar. Math se realiza mediante la creación de nuevos objetos de enteros.

  • Los objetos como son listas mutable . Esto significa que el contenido del objeto se pueden cambiar por nada que apunta al objeto. Por ejemplo, x = []; y = x; x.append(10); print y imprimirá [10]. se creó la lista vacía. Tanto "x" y el punto "y" a la misma lista. El append muta método (actualizaciones) el objeto de lista (como la adición de un registro a una base de datos) y el resultado es visible para ambos "x" e "y" (al igual que una actualización de la base de datos sería visible para cada conexión a la base de datos).

La esperanza de que clarifica el problema para usted.

Técnicamente, Python utiliza siempre pase por los valores de referencia . Voy a repetir mi otra respuesta para mantener a mi declaración.

Python siempre utiliza valores pasar 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 unido al valor de referencia. Siempre.

Puede pensar en un valor de referencia como la dirección del objeto de destino. La dirección se eliminan las referencias de forma automática cuando se utiliza. De esta manera, el trabajo con el valor de referencia, parece que trabaja directamente con el objeto de destino. Pero siempre es una referencia en el medio, un paso más para saltar a la meta.

Aquí es el ejemplo que demuestra que Python usa pasando por referencia:

ejemplo ilustrado de pasar el argumento

Si el argumento se pasa por valor, el lst exterior no podía ser modificado. El verde son los objetos de destino (el negro es el valor almacenado en el interior, el rojo es el tipo de objeto), el amarillo es el de memoria con el valor de referencia en el interior - dibujado como la flecha. La flecha sólida azul es el valor de referencia que se pasa a la función (a través de la ruta de flecha azul discontinua). El color amarillo oscuro feo es el diccionario interno. (En realidad, podría extraerse también como una elipse verde. El color y la forma única dice que es interno.)

Puede utilizar el href="http://docs.python.org/3.3/library/functions.html#id" rel="noreferrer"> id() función

En los lenguajes compilados, una variable es un espacio de memoria que es capaz de capturar el valor del tipo. En Python, una variable es un nombre (capturado internamente como una cadena) unido a la variable de referencia que mantiene el valor de referencia al objeto de destino. El nombre de la variable es la clave en el diccionario interno, la parte del valor de ese elemento del diccionario almacena el valor de referencia al objetivo.

Los valores de referencia se ocultan en Python. No hay ningún tipo explícita del usuario para almacenar el valor de referencia. Sin embargo, se puede utilizar un elemento de la lista (o elemento en cualquier otro tipo de contenedor adecuado) como variable de referencia, ya que todos los contenedores hacen almacenar los elementos también como referencias a los objetos de destino. En otras palabras, los elementos son en realidad no contenían el interior del contenedor - sólo las referencias a los elementos son

.

Un truco sencillo que normalmente uso es sólo para 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).

No hay variables en Python

La clave para comprender el paso de parámetros es dejar de pensar en "variables".Hay nombres y objetos en Python y juntos parecen variables, pero es útil distinguir siempre los tres.

  1. Python tiene nombres y objetos.
  2. La asignación vincula 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 une el nombre b al mismo objeto que el nombre x está actualmente obligado a.Posteriormente, el nombre b no tiene nada que ver con el nombre x ya no.

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

Cómo leer el ejemplo en la pregunta.

En el código que se muestra en la pregunta, la declaración self.Change(self.variable) une el nombre var (en el ámbito de la función Change) al objeto que contiene el valor 'Original' y la tarea var = 'Changed' (en el cuerpo de la función Change) asigna ese mismo nombre nuevamente:a algún otro objeto (que también contiene una cuerda, pero podría haber sido algo completamente diferente).

Cómo pasar por referencia

(editar 2019-04-28)

Entonces, si lo que desea cambiar es un objeto mutable, no hay problema, ya que todo se pasa efectivamente por referencia.

Si es un inmutable objeto (por ej.un bool, número, cadena), el camino a seguir es envolverlo en un objeto mutable.
La solución rápida y sucia para esto es una lista de un elemento (en lugar de self.variable, aprobar [self.variable] y en la función modificar var[0]).
Cuanto más pitónico El enfoque sería introducir una clase trivial de un solo atributo.La función recibe una instancia de la clase y manipula el atributo.

(edit - Blair ha actualizado su respuesta enormemente popular por lo que ahora es preciso)

Creo que es importante tener en cuenta que el puesto actual con el mayor número de votos (por Blair Conrad), mientras que ser correcta con respecto a su resultado, es engañosa y está en el límite incorrectos basado en sus definiciones. Si bien hay muchos idiomas (como C) que permiten al usuario a cualquiera de pasar por referencia o de paso por valor, Python no es uno de ellos.

respuesta apunta

de David Cournapeau a la respuesta real y explica por qué el comportamiento en el puesto de Blair Conrad parece ser correcta, mientras que las definiciones no son.

En la medida en que Python es paso por valor, todos los idiomas están paso por valor, ya que algunos pieza de datos (ya sea un "valor" o una "referencia") deben enviarse. Sin embargo, eso no quiere decir que Python es paso por valor, en el sentido de que un programador C habría pensado de ella.

Si desea que el comportamiento, la respuesta de Blair Conrad está muy bien. Pero si quieres saber los detalles prácticos de qué Python no es ni pase por el valor o el paso por referencia, leer la respuesta de David Cournapeau.

Tienes algunas realmente buenas respuestas 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 la variable var titulado en el Change método se asigna una referencia a self.variable, y se asigna una cadena de inmediato a var. Ya no se apunta a self.variable. El siguiente fragmento de código muestra lo que ocurriría si se modifica la estructura de datos apuntado 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 pudiera aclarar más a fondo.

esquema pase por la asignación de Python no es exactamente la misma opción de parámetros de referencia C ++ 's como, pero resulta ser muy similar al modelo argumento de paso de la lengua C (y otros) en la práctica:

  • inmutables argumentos se pasan de manera efectiva “ por valor .” Los objetos tales como enteros y cadenas se pasan por referencia a un objeto en lugar de mediante la copia, pero debido a que no pueden cambiar los objetos inmutables en el lugar de todos modos, la efecto es muy parecido a hacer una copia.
  • mutables argumentos se pasan de manera efectiva “ por el puntero .” Los objetos tales como listas y los diccionarios también se pasan por referencia objeto, que es similar a la forma C pases de matrices como objetos punteros-mutables se pueden cambiar en su lugar en la función, al igual que las matrices C.

Como se puede afirmar es necesario tener un objeto mutable, pero permítanme sugerir que compruebe sobre las variables globales, ya que pueden ayudarle o incluso 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
<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

Una gran cantidad de ideas en respuestas aquí, pero creo que un punto adicional no se menciona claramente aquí de forma explícita. Citando de documentación de Python https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

"En Python, las variables que sólo se hace referencia dentro de una función son implícitamente global. Si una variable se le asigna un nuevo valor en cualquier lugar dentro del cuerpo de la función, se supone que es un local. Si una variable es cada vez le asigna un nuevo valor en el interior la función, la variable es implícitamente locales, y tiene que declarar explícitamente como 'global'. Aunque un poco sorprendente en un primer momento, un momento de consideración explica esto. Por un lado, lo que requiere global para variables asignadas proporciona una barra contra los efectos secundarios no deseados. Por otro lado, si se requiere global para todas las referencias a nivel mundial, que estaría utilizando mundial todo el tiempo. Habría que declarar como mundial cada referencia a una función incorporada o a un componente de un módulo importado. Este desorden iría en contra de la utilidad de la declaración global para la identificación de los efectos secundarios ".

Aún cuando pasa un objeto mutable a una función de esto todavía se aplica. Y me explica claramente la razón de la diferencia de comportamiento entre la asignación al objeto y que opera en el objeto en la función.

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

La asignación a una variable global que no se declara mundial, por lo tanto crea un nuevo objeto local y rompe el vínculo con el objeto original.

Esta es la sencilla (espero) explicación de la pass by object concepto que se utiliza en Python.
Cada vez que se pasa un objeto a la función, el objeto en sí se pasa (objeto en Python es en realidad lo que podríamos llamar un valor en otros lenguajes de programación) no la referencia a este objeto. En otras palabras, cuando se llama:

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

my_list = [0, 1]
change_me(my_list)

El objeto real - [0, 1] (que se llama un valor en otros lenguajes de programación) se está pasando. Así que, de hecho, la función change_me tratará de hacer algo como:

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

que, evidentemente, no va a cambiar el objeto pasa a la función. Si la función era la siguiente:

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

A continuación, la llamada se traduciría en:

[0, 1].append(2)

que obviamente va a cambiar el objeto. Esta respuesta lo explica muy bien.

Además de todas las grandes explicaciones sobre cómo esto funciona en Python, no veo una sugerencia simple para el problema. Como parece que hacer crear objetos e instancias, la forma Pythonic de manejar variables de instancia y cambiarlas es la siguiente:

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

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

En los métodos de instancia, que normalmente se refieren a self acceder a los atributos de instancia. Es normal que establecer los atributos de instancia en __init__ y leer o cambiarlos en los métodos de instancia. Por eso también se pasa del als self el primer argumento de def Change.

Otra solución sería la creación de un método estático como sigue:

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

Hay un pequeño truco para pasar un objeto por referencia, a pesar de que el idioma no significa que sea posible. Funciona en Java también, es la lista con un elemento. ; -)

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

Es un truco feo, pero funciona. ;-P

He utilizado el siguiente método para convertir rápidamente un par de códigos Fortran a Python. Es cierto que no se pasan por referencia como la pregunta original se planteó, pero es un trabajo sencillo alrededor en algunos casos.

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

Mientras que pase por referencia hay nada que encaja muy bien en pitón y debe ser rara vez se utiliza, hay algunas soluciones que realmente puede trabajar para conseguir el objeto actualmente asignado a una variable local o incluso volver a asignar una variable local desde el interior de una función llamada.

La idea básica es tener una función que puede hacer que el acceso y se pueden pasar como objeto en otras funciones o almacenada en una clase.

Una forma es utilizar global (para las variables globales) o nonlocal (para las variables locales de una función) en una función de contenedor.

def change(wrapper):
    wrapper(7)

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

La misma idea funciona para leer y deleting una variable.

Por sólo leer incluso hay un camino más corto de usar sólo lambda: x que devuelve un invocable que cuando se le devuelve el valor actual de x. Esto es algo así como "llamada por nombre" que se utiliza en las lenguas en el pasado distante.

Pasando 3 envolturas de acceder a una variable es un poco difícil de manejar por lo que aquellos se puede envolver en una clase que tiene un atributo de 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

Pitones "reflexión" apoyo hace que sea posible obtener un objeto que es capaz de reasignar un nombre / variable en un ámbito determinado, sin definir las funciones de forma explícita en ese ámbito:

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)

A continuación, la clase ByRef se ajusta un acceso diccionario. Así atributo acceso a wrapped se traduce a un elemento de acceso en el diccionario pasado. Pasando el resultado de la locals orden interna y el nombre de una variable local esto termina el acceso a una variable local. La documentación de Python como de 3.5 informa que cambiar el diccionario no podría funcionar, pero parece que funciona para mí.

dada la forma de pitón trata los valores y las referencias a ellos, la única forma en que puede hacer referencia a un atributo de instancia es arbitraria por su nombre:

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

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

en el código real que, por supuesto, añadir la comprobación de errores en las operaciones de búsqueda dict.

Pass por referencia en Python es bastante diferente del concepto de pase por referencia en C ++ / Java.

  • Java & C #: tipos primitivos (incluyen string) pasar por valor (copia), tipo de referencia se pasa por referencia (copia dirección) por lo que todos los cambios realizados en el parámetro en la función llamada son visibles para la persona que llama.
  • C ++: Tanto pasar por referencia o pasar por valor están permitidas. Si un parámetro se pasa por referencia, puede modificarlo o no, dependiendo de si el parámetro se pasa como const o no. Sin embargo, const o no, el parámetro mantiene la referencia al objeto y de referencia no se puede asignar para apuntar a un objeto diferente dentro de la función llamada.
  • Python: Python es “pasar-por-objeto de referencia”, de la que a menudo se dice: “Objeto referencias se pasan por valor” [Lea aquí] 1 . Tanto la persona que llama y la función se refieren al mismo objeto, pero el parámetro de la función es una variable nueva que recién está llevando a cabo una copia del objeto en la persona que llama. Al igual que C ++, un parámetro puede ser modificado o no en función - Esto depende del tipo de objeto pasado. p.ej; Un tipo de objeto inmutable no puede ser modificado en la función llamada mientras que un objeto mutable puede ser ya sea actualizada o re-inicializado. Una diferencia fundamental entre actualizar o volver a asignar / re-inicializar la variable mutable es que el valor actualizado se refleja de nuevo en la función llamada mientras que el valor reinicializado no lo hace. Ámbito de aplicación de cualquier asignación de nuevo objeto a una variable mutable es local a la función en el pitón. Los ejemplos proporcionados por @ Blair-Conrad son grandes para entender esto.

Debido a que su ejemplo pasa a ser orientado a objetos, se puede hacer el siguiente cambio para lograr un resultado similar:

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'

Puesto que parece que se menciona en ninguna parte un enfoque para simular referencias como se conoce a partir de, por ejemplo, C ++ es utilizar una función de "actualización" y pasar que en lugar de la variable real (o más bien, "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

Esto es sobre todo útil para "fuera sólo referencias" o en una situación con múltiples hilos / procesos (al hacer la función de actualización hilo / multiproceso seguro).

Obviamente, lo anterior no permite lectura es el valor, solamente su actualización.

Usted sólo puede usar una clase vacía como una instancia para almacenar objetos de referencia, porque internamente atributos de objeto se almacenan en un diccionario ejemplo. Vea el ejemplo.

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)

Dado que los diccionarios se pasan por referencia, se puede utilizar una variable dict para almacenar cualquier valor que se hace referencia en su interior.

# 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top