Este es mi segundo día de aprendizaje de Python (conozco los conceptos básicos de C ++ y algo de OOP), y tengo una ligera confusión con respecto a las variables en Python.
Así es como los entiendo actualmente:
Las variables de Python son referencias (¿o punteros?) A objetos (que son mutables o inmutables). Cuando tenemos algo así num = 5
, el objeto inmutable 5
se crea en algún lugar de la memoria y el par de referencia nombre-objeto num
se crea en un cierto espacio de nombres. Cuando lo tenemos a = num
, no se copia nada, pero ahora ambas variables se refieren al mismo objeto y a
se agregan al mismo espacio de nombres.
Aquí es donde mi libro, Automatizar las cosas aburridas con Python , me confunde. Como es un libro para principiantes, no menciona objetos, espacios de nombres, etc., e intenta explicar el siguiente código:
>>> spam = 42
>>> cheese = spam
>>> spam = 100
>>> spam
100
>>> cheese
42
La explicación que ofrece es exactamente la misma que la de un libro de C ++, lo que no me satisface ya que estamos tratando con referencias / punteros a objetos. Entonces, en este caso, supongo que en la tercera línea, como los enteros son inmutables, spam
se le está asignando un puntero / referencia completamente nuevo a una ubicación diferente en la memoria, es decir, la memoria a la que apuntaba inicialmente no se modificó. De ahí que nos cheese
refiramos al objeto inicial al que se refiere spam
. ¿Es esta la explicación correcta?
42
enspam
, Ahora almacenastespam
en medios de quesocheese = 42
, Después de eso reemplazastespam
a100
, No editaste,cheese
por esocheese
está quieto42
.spam
en el número 42. Luego, pegaste la etiquetacheese
en la cosa etiquetada comospam
(no encima de la etiqueta en sí, claro). Luego lespam
quitó la etiqueta y la puso en el número 100.Respuestas:
Como desarrollador de C ++, puede pensar en las variables de Python como punteros.
Por lo tanto, cuando escribe
spam = 100
, esto significa que "asigna el puntero", que antes apuntaba al objeto42
, para que apunte al objeto100
.Anteriormente,
cheese
se asignó para apuntar al mismo objeto que sespam
señaló, que resultó ser42
en ese momento. Como no ha modificadocheese
, todavía apunta a42
.La inmutabilidad no tiene nada que ver con esto en este caso, ya que la asignación de puntero no cambia nada sobre el objeto al que se apunta.
fuente
A mi modo de ver, hay diferentes visiones de un idioma.
Desde la perspectiva del abogado del lenguaje, las variables de Python siempre "apuntan" a un objeto. Sin embargo, a diferencia de Java y C ++, el comportamiento de == <=> = etc depende del tipo de tiempo de ejecución de los objetos a los que apuntan las variables. Además, en Python, la gestión de la memoria está a cargo del lenguaje.
Desde la perspectiva de un programador práctico, podemos tratar el hecho de que los enteros, cadenas, tuplas, etc. son objetos inmutables * en lugar de valores simples como un detalle irrelevante. La excepción es cuando se almacenan grandes cantidades de datos numéricos, es posible que deseemos usar tipos que puedan almacenar los valores directamente (por ejemplo, matrices numpy) en lugar de tipos que terminarán con una matriz llena de referencias a objetos pequeños.
Desde la perspectiva de los implementadores, la mayoría de los lenguajes tienen algún tipo de regla como si, de modo que si los comportamientos especificados son correctos, la implementación es correcta independientemente de cómo se hagan las cosas bajo el capó.
Entonces, sí, su explicación es correcta desde la perspectiva de un abogado de idiomas. Su libro es correcto desde la perspectiva de un programador práctico. Lo que realmente hace una implementación depende de la implementación. En cpython, los enteros son objetos reales, aunque los enteros de valor pequeño se toman de un grupo de caché en lugar de crearlos de nuevo. No estoy seguro de qué hacen las otras implementaciones (por ejemplo, pypy y jython).
* observe la distinción entre objetos mutables e inmutables aquí. Con un objeto mutable tenemos que tener cuidado de tratarlo "como un valor" porque algún otro código podría mutarlo. Con un objeto inmutable no tenemos tales preocupaciones.
fuente
Es cierto que puede utilizar más o menos variables como punteros. Sin embargo, el código de ejemplo ayudaría mucho a explicar cómo funciona esto realmente.
Primero, utilizaremos mucho la
id
función:Es probable que esto devuelva diferentes valores absolutos en su máquina.
Considere este ejemplo:
>>> foo = 'a string' >>> id(foo) 4565302640 >>> bar = 'a different string' >>> id(bar) 4565321816 >>> bar = foo >>> id(bar) == id(foo) True >>> id(bar) 4565302640
Puedes ver eso:
cuando cambiamos el valor de foo, se asigna a una identificación diferente:
>>> foo = 42 >>> id(foo) 4561661488 >>> foo = 'oh no' >>> id(foo) 4565257832
Una observación interesante también es que los enteros tienen implícitamente esta funcionalidad hasta 256:
>>> a = 100 >>> b = 100 >>> c = 100 >>> id(a) == id(b) == id(c) True
Sin embargo, más allá de 256, esto ya no es cierto:
>>> a = 256 >>> b = 256 >>> id(a) == id(b) True >>> a = 257 >>> b = 257 >>> id(a) == id(b) False
sin embargo, la asignación
a
ab
mantendrá la identificación igual que se muestra antes:>>> a = b >>> id(a) == id(b) True
fuente
Python no es paso por referencia ni paso por valor. Las variables de Python no son punteros, no son referencias, no son valores. Las variables de Python son nombres .
Piense en ello como "pasar-por-alias" si necesita el mismo tipo de frase, o posiblemente "pasar-por-objeto", porque puede mutar el mismo objeto de cualquier variable que lo indique, si es mutable, pero reasignación de una variable (alias) solo cambia esa variable.
El nombre de una variable de Python es una clave en el espacio de nombres global (o local), que es efectivamente un diccionario. El valor subyacente es algún objeto en la memoria. La asignación le da un nombre a ese objeto. La asignación de una variable a otra variable significa que ambas variables son nombres para el mismo objeto. La reasignación de una variable cambia qué objeto es nombrado por esa variable sin cambiar la otra variable. Ha movido la etiqueta, pero no ha cambiado el objeto anterior ni ninguna otra etiqueta.
En el código C subyacente de la implementación de CPython, cada objeto de Python es un
PyObject*
, por lo que puede pensar que funciona como C si solo tuviera punteros a datos (sin punteros a punteros, sin valores pasados directamente).fuente
Cuando ejecuta
spam = 100
python crea un objeto más en la memoria pero no cambia el existente. así que todavía tienes el punterocheese
al 42 yspam
al 100fuente
Lo que está sucediendo en
spam = 100
línea es el reemplazo del valor anterior (puntero al objeto de tipoint
con valor42
) con otro puntero a otro objeto (tipoint
, valor100
)fuente
new Class()
sintaxis en C ++. Además, en Python, cualquier cosa es una instancia deobject
clase / subclase.Como mencionó @DeepSpace en los comentarios, Ned Batchelder hace un gran trabajo desmitificando variables (nombres) y asignaciones a valores en un blog, desde el cual pronunció una charla en PyCon 2015, Facts and Myths about Python nombres y valores . Puede ser útil para los Pythonistas en cualquier nivel de dominio.
fuente
En Python, una variable contiene la referencia al objeto . Un objeto es una porción de memoria asignada que contiene un valor y un encabezado . El encabezado del objeto contiene su tipo y un contador de referencia que indica la cantidad de veces que se hace referencia a este objeto en el código fuente para que Garbage Collection pueda identificar si un objeto se puede recolectar.
Ahora, cuando asigna valores a una variable, Python realmente asigna referencias que son punteros a ubicaciones de memoria asignadas a objetos:
# x holds a reference to the memory location allocated for # the object(type=string, value="Hello World", refCounter=1) x = "Hello World"
Ahora, cuando asigna objetos de diferente tipo a la misma variable, en realidad cambia la referencia para que apunte a un objeto diferente (es decir, una ubicación de memoria diferente). En el momento en que asigne una referencia diferente (y por lo tanto, un objeto) a una variable, el recolector de basura recuperará inmediatamente el espacio asignado al objeto anterior, asumiendo que ninguna otra variable del código fuente hace referencia a él:
# x holds a reference to the memory location allocated for # the object(type=string, value="Hello World", refCounter=1) x = "Hello World" # Now x holds the reference to a different object(type=int, value=10, refCounter=1) # and object(type=string, value="Hello World", refCounter=0) -which is not refereced elsewhere # will now be garbage-collected. x = 10
Llegando a tu ejemplo ahora,
spam
contiene la referencia al objeto (tipo = int, valor = 42, refCounter = 1):>>> spam = 42
Ahora
cheese
también tendrá la referencia al objeto (tipo = int, valor = 42, refCounter = 2)>>> cheese = spam
Ahora el spam contiene una referencia a un objeto diferente (type = int, value = 100, refCounter = 1)
>>> spam = 100 >>> spam 100
Pero el queso seguirá apuntando al objeto (tipo = int, valor = 42, refCounter = 1)
>>> cheese 42
fuente
Cuando almacena
spam = 42
, crea un objeto en la memoria. Luego asignacheese = spam
, Asigna el objeto referenciado porspam
acheese
. Y finalmente, cuando cambiaspam = 100
, cambia solospam
objeto. Entoncescheese = 42
.fuente
spam
acheese
. No se crean nuevos objetos.La página de función numpy.copy () tiene una explicación
https://docs.scipy.org/doc/numpy/reference/generated/numpy.copy.html
El ejemplo que da es el siguiente:
Cree una matriz x, con una referencia y y una copia z:
x = np.array([1, 2, 3]) y = x z = np.copy(x)
Tenga en cuenta que, cuando modificamos x, y cambia, pero no z:
x[0] = 10 x[0] == y[0] True x[0] == z[0] False
fuente