2011-12-11 8 views
7

No es este código:Asignación de objetos y tipos fundamentales

# assignment behaviour for integer 
a = b = 0 
print a, b # prints 0 0 
a = 4 
print a, b # prints 4 0 - different! 

# assignment behaviour for class object 
class Klasa: 
    def __init__(self, num): 
     self.num = num 

a = Klasa(2) 
b = a 
print a.num, b.num # prints 2 2 
a.num = 3 
print a.num, b.num # prints 3 3 - the same! 

Preguntas:

  1. Por qué operador de asignación funciona de forma diferente para el tipo fundamental y objeto de clase (por tipos fundamentales que copia de valor, para el objeto de clase que copia por referencia)?
  2. ¿Cómo se copian objetos de clase solo por valor?
  3. Cómo hacer referencias para tipos fundamentales como en C++ int & b = a?
+2

Fuera del lenguaje Python, los términos por referencia/por valor ya son confusos y confusos. Dentro de Python, cuyo modelo de datos y modelo de ejecución son tan especiales, estos términos son aún más confusos y confusos y deben evitarse. Esa es mi opinión, pero tenga en cuenta que no soy un experto en Python. Ver (http://stackoverflow.com/a/986145/551449) y muchas otras publicaciones y blogs sobre este tema. Parece que necesita estudiar un poco más los datos y modelos de ejecución de Python – eyquem

Respuesta

11

Ésta es una piedra de tropiezo para muchos usuarios de Python. La semántica de referencia del objeto es diferente de lo que los programadores de C utilizan.

Tomemos el primer caso. Cuando dice a = b = 0, se crea un nuevo objeto int con el valor 0 y se crean dos referencias (una es a y otra es b). Estas dos variables apuntan al mismo objeto (el entero que creamos). Ahora, ejecutamos a = 4. Se crea un nuevo objeto int de valor 4 y a para señalarlo. Esto significa que el número de referencias a 4 es uno y el número de referencias a 0 se ha reducido en uno.

Compare esto con a = 4 en C donde se escribe el área de memoria que a "señala". a = b = 4 en C significa que 4 está escrito en dos pedazos de memoria: uno para a y otro para b.

Ahora el segundo caso, a = Klass(2) crea un objeto del tipo Klass, incrementa su recuento de referencias en uno y hace que a apunte a él. b = a simplemente toma lo que a señala, hace que b señale lo mismo e incrementa el recuento de referencia de la cosa en uno. Es lo mismo que pasaría si lo hiciera a = b = Klass(2). Tratar de imprimir a.num y b.num es el mismo ya que está desreferenciando el mismo objeto e imprimiendo un valor de atributo. Puede usar la función integrada id para ver que el objeto sea el mismo (id(a) y id(b) devolverá el mismo identificador). Ahora, cambia el objeto asignando un valor a uno de sus atributos. Como a y b apuntan al mismo objeto, es de esperar que el cambio en el valor sea visible cuando se accede al objeto a través del a o b. Y así es exactamente.

Ahora, para las respuestas a sus preguntas.

  1. El operador de asignación no funciona de manera diferente para estos dos. Todo lo que hace es agregar una referencia a RValue y hace que LValue apunte a ella. Es siempre "por referencia" (aunque este término tiene más sentido en el contexto del paso de parámetros que las asignaciones simples).
  2. Si desea copias de objetos, use copy module.
  3. Como dije en el punto 1, cuando haces una tarea, siempre cambias las referencias. La copia nunca se realiza a menos que usted lo solicite.
+0

Creo que en la expresión "pasar por referencia", la palabra "referencia" designa un número == una dirección de memoria. Pero en la mayoría de su texto, toma la palabra "referencia" con el significado de "un trozo de memoria que contiene un número que es la dirección de una ubicación de memoria", es decir, "referencia" y luego se emplea como sinónimo de "puntero". Como "puntero" y "referencia" son palabras que tienen un significado flotante según el idioma considerado, y Python tiene datos especiales y modelos de ejecución, hay un olor a ambigüedad confusa en el texto, como en la gran mayoría de los textos sobre este tema – eyquem

+0

Cuándo usted escribe '' a'', ¿representa el objeto (una estructura de bits en la memoria), la referencia (porción de memoria que actúa como un cuadro) al objeto o el identificador? – eyquem

+0

eyquem. Estoy de acuerdo. Probablemente debería definir los términos antes de hablar de esto, ya que es un tema potencialmente confuso. –

1

No funciona de manera diferente. En su primer ejemplo, ha cambiado a para que a y b referencian diferentes objetos. En su segundo ejemplo, no lo hizo, por lo que a y b aún hacen referencia al mismo objeto.

Los enteros, por cierto, son inmutables. No puedes modificar su valor. Todo lo que puede hacer es crear un nuevo entero y volver a enlazar su referencia. (Como lo hizo en el primer ejemplo)

5

Citando Data Model

objetos son la abstracción de Python para los datos. Todos los datos en un programa Python están representados por objetos o por relaciones entre objetos. (En un sentido, y de conformidad con el modelo de una de Von Neumann “ordenador de programa almacenado ,” código está también representada por objetos.)

Desde el punto de vista de Python, Fundamental data type es fundamentalmente diferente de C/C++ . Se utiliza para asignar tipos de datos C/C++ a Python. Y, por el momento, dejemos la discusión y consideremos el hecho de que todos los datos son objeto y son manifestación de alguna clase. Cada objeto tiene una identificación (algo así como dirección), valor y un tipo.

Todos los objetos se copian por referencia. Por ejemplo,

>>> x=20 
>>> y=x 
>>> id(x)==id(y) 
True 
>>> 

La única forma de tener una nueva instancia es creando una.

>>> x=3 
>>> id(x)==id(y) 
False 
>>> x==y 
False 

Esto puede sonar complicado en primera instancia, pero para simplificar un poco, Python hizo algunos tipos inmutables. Por ejemplo, no puede cambiar un string. Tienes que cortarlo y crear un nuevo objeto de cadena.

A menudo, copiar por referencia arroja resultados inesperados para ej.

x=[[0]*8]*8 puede darle la sensación de que crea una lista bidimensional de 0 s. Pero, de hecho, crea una lista de la referencia del mismo objeto de lista [0] s. Así que hacer x [1] [1] terminaría cambiando todas las instancias duplicadas al mismo tiempo.

El módulo Copy proporciona un método llamado deepcopy para crear una nueva instancia del objeto en lugar de una instancia superficial. Esto es beneficioso cuando tiene la intención de tener dos objetos distintos y manipularlos por separado tal como lo pretendía en su segundo ejemplo.

extender su ejemplo

>>> class Klasa: 
    def __init__(self, num): 
     self.num = num 


>>> a = Klasa(2) 
>>> b = copy.deepcopy(a) 
>>> print a.num, b.num # prints 2 2 
2 2 
>>> a.num = 3 
>>> print a.num, b.num # prints 3 3 - different! 
3 2 
+0

+1 para las referencias a la documentación. –

+0

@Abhijit Cuando escribe _ "copiado por referencia" _ para el ejemplo '' x = 20'' luego '' y = x'', ¿qué se copia? Personalmente, creo que estrictamente no hay nada que se copia y es por eso que el uso de "copiar por" no tiene sentido en Python en algunas ocasiones, tal vez en todas. – eyquem

+0

Se copian los datos internos (un puntero, específicamente un PyObject *, en la implementación C) que se usa para hacer que las variables hagan referencia a los valores. :) –

1

Supongamos que usted y yo tenemos un amigo en común. Si decido que ya no me gusta, ella sigue siendo tu amiga. Por otro lado, si le doy un regalo, su amigo recibió un regalo.

La asignación no copia nada en Python, y "copiar por referencia" es algo entre incómodo y sin sentido (como usted señala en uno de sus comentarios). La asignación hace que una variable comience a referirse a un valor. No hay "tipos fundamentales" separados en Python; mientras que algunos de ellos están integrados, int sigue siendo una clase.

En ambos casos, misiones hace que la variable para hacer referencia a lo que sea que la mano del lado derecho se evalúa como. El comportamiento que está viendo es exactamente lo que debería esperar en ese entorno, según la metáfora. Si su "amigo" es int o Klasa, la asignación a un atributo es fundamentalmente diferente de la reasignación de la variable a una instancia completamente diferente, con el comportamiento correspondiente diferente.

La única diferencia real es que el int no tiene ningún atributo que pueda asignar. (Esa es la parte donde la implementación en realidad tiene que hacer un poco de magia para restringirlo).

Está confundiendo dos conceptos diferentes de una "referencia". El C++ T& es una cosa mágica que, cuando se asigna a, actualiza el objeto referido en el lugar, y no la referencia en sí misma; que nunca se puede volver a "resetear" una vez que se inicializa la referencia. Esto es útil en un lenguaje donde la mayoría de las cosas son valores. En Python, todo es una referencia para empezar. La referencia Pythonic es más como un puntero siempre válido, nunca nulo, no utilizable para aritmética, sin referencia automática. La asignación hace que la referencia comience a referirse completamente a algo diferente. No puede "actualizar el objeto referido en el lugar" reemplazándolo al por mayor, porque los objetos de Python simplemente no funcionan así. Por supuesto, puede actualizar su estado interno jugando con sus atributos (si hay alguno accesible), pero esos atributos son, en sí mismos, también todas las referencias.

Cuestiones relacionadas