De acuerdo, lo primero es lo primero.
No existe tal cosa como "declaración de variable" o "inicialización de variable" en Python.
Hay simplemente lo que llamamos "asignación", pero probablemente deberíamos llamar simplemente "nombrar".
Asignación significa "este nombre en el lado izquierdo ahora se refiere al resultado de evaluar el lado derecho, sin importar a qué se refería antes (si acaso)".
foo = 'bar'
foo = 2 * 3
Como tal, los nombres de Python (un término mejor que "variables", posiblemente) no tienen tipos asociados; los valores lo hacen. Puede volver a aplicar el mismo nombre a cualquier cosa independientemente de su tipo, pero la cosa aún tiene un comportamiento que depende de su tipo. El nombre es simplemente una forma de referirse al valor (objeto). Esto responde a su segunda pregunta: no crea variables para mantener un tipo personalizado. No crea variables para contener ningún tipo en particular. No "crea" variables en absoluto. Le das nombres a los objetos.
Segundo punto: Python sigue una regla muy simple cuando se trata de clases, que en realidad es mucho más consistente que lo que hacen lenguajes como Java, C ++ y C #: todo lo declarado dentro del class
bloque es parte de la clase . Entonces, las funciones ( def
) escritas aquí son métodos, es decir, parte del objeto de la clase (no almacenado por instancia), al igual que en Java, C ++ y C #; pero otros nombres aquí también son parte de la clase. Nuevamente, los nombres son solo nombres y no tienen tipos asociados, y las funciones también son objetos en Python. Así:
class Example:
data = 42
def method(self): pass
Las clases también son objetos en Python.
Así que ahora hemos creado un objeto llamado Example
, que representa la clase de todas las cosas que son Example
s. Este objeto tiene dos atributos proporcionados por el usuario (en C ++, "miembros"; en C #, "campos o propiedades o métodos"; en Java, "campos o métodos"). Uno de ellos se nombra data
y almacena el valor entero 42
. El otro se nombra method
y almacena un objeto de función. (Hay varios atributos más que Python agrega automáticamente).
Sin embargo, estos atributos todavía no forman parte del objeto. Básicamente, un objeto es solo un conjunto de más nombres (los nombres de los atributos), hasta que se llega a cosas que ya no se pueden dividir. Por lo tanto, los valores se pueden compartir entre diferentes instancias de una clase, o incluso entre objetos de diferentes clases, si lo configura deliberadamente.
Creemos una instancia:
x = Example()
Ahora tenemos un objeto separado llamado x
, que es una instancia de Example
. Los data
y method
no son en realidad parte del objeto, pero aún podemos buscarlos a través x
de algo de magia que Python hace detrás de escena. Cuando miramos hacia arriba method
, en particular, obtendremos un "método vinculado" (cuando lo llamamos, x
se pasa automáticamente como self
parámetro, lo que no puede suceder si miramos hacia arriba Example.method
directamente).
¿Qué pasa cuando intentamos usar x.data
?
Cuando lo examinamos, primero se busca en el objeto. Si no se encuentra en el objeto, Python busca en la clase.
Sin embargo, cuando asignamos a x.data
, Python creará un atributo en el objeto. Será no reemplazar el atributo de clase.
Esto nos permite realizar la inicialización de objetos . Python llamará automáticamente al __init__
método de la clase en las nuevas instancias cuando se creen, si están presentes. En este método, simplemente podemos asignar atributos para establecer valores iniciales para ese atributo en cada objeto:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
Ahora debemos especificar un name
cuando creamos un Example
, y cada instancia tiene el suyo name
. Python ignorará el atributo de clase Example.name
cada vez que busquemos el .name
de una instancia, porque el atributo de la instancia se encontrará primero.
Una última advertencia: ¡la modificación (mutación) y la asignación son cosas diferentes!
En Python, las cadenas son inmutables. No se pueden modificar. Cuando tu lo hagas:
a = 'hi '
b = a
a += 'mom'
No cambia la cadena 'hola' original. Eso es imposible en Python. En su lugar, crea una nueva cadena 'hi mom'
y hace a
que deje de ser un nombre para 'hi '
y comience a ser un nombre para en su 'hi mom'
lugar. También hicimos b
un nombre para 'hi '
, y después de volver a aplicar el a
nombre, b
sigue siendo un nombre para 'hi '
, porque 'hi '
todavía existe y no se ha cambiado.
Pero las listas se pueden cambiar:
a = [1, 2, 3]
b = a
a += [4]
Ahora b
es [1, 2, 3, 4] también, porque hicimos b
un nombre para la misma cosa que a
nombró, y luego cambiamos esa cosa. No creamos una nueva lista para a
nombrar, porque Python simplemente trata de manera +=
diferente a las listas.
Esto es importante para los objetos porque si tuviera una lista como atributo de clase y usara una instancia para modificar la lista, el cambio se "vería" en todas las demás instancias. Esto se debe a que (a) los datos son en realidad parte del objeto de clase y no un objeto de instancia; (b) debido a que estaba modificando la lista y no haciendo una asignación simple, no creó un nuevo atributo de instancia que ocultaba el atributo de clase.