El tipado dinámico
Escrito el Sábado 13 de Junio del 2009 por Ealdor
Hasta ahora hemos estado utilizando variables sin declarar su existencia o su tipo. Cuando escribimos por ejemplo 'a=3', ¿cómo sabe Python que la 'a' debería funcionar como un entero?, ¿cómo sabe Python que es la 'a'?.
Aquí es donde entra en juego el tipado dinámico. En Python, los tipos son determinados automaticamente durante la ejecución, no en respuesta a declaraciones en tu código. Esto significa que tu nunca declaras variables antes de tiempo.
[A] Variables, objetos y referencias
Creacion de la variable: una variable, como 'a', es creada cuando tu código le asigna un valor. Los futuros asignamientos cambian el valor del nombre ya creado.
Tipos de variable: una variable nunca tiene ningúna información del tipo asociada a ella. La noción de tipo reside en los objetos, no en los nombres. Las variables son de naturaleza genérica.
Uso de variable: cuando una variable aparece en una expresion, se reemplaza inmediatamente por el objeto al que actualmente se refiere. Además, todas las variables deben ser explicitamente asignadas antes de que puedan utilizarse; usar variables no asignadas da lugar a errores.
Por ejemplo, cuando nosotros ponemos:
>>> a = 3
Se traduce a lo siguientes pasos:
1.- Se crea un objeto para representar el valor '3'.
2.- Se crea la variable 'a', si esta no existe ya.
3.- Se enlaza la variable 'a' al nuevo objeto '3'.
Las variables siempre se enlazan a objetos, y nunca a otras variables, pero objetos largos pueden apuntar a otros objetos (por ejemplo, una lista de objetos tiene enlaces a los objetos que contiene).

Los enlaces desde las variables a los objetos son llamados "referencias" en Python (una referencia es un tipo de asociación, implementada como un puntero en memoria).
[B] Los tipos residen con los objetos, no con las variables
Veamos que ocurre si asignamos una variable en multiples ocasiones:
>>> a = 3
>>> a = 'spam'
>>> a = 1.23
En Python las cosas funcionan muy simples: los nombres no tienen tipos, los tipos residen en los objetos, no en los nombres. Debido a que las variables no tienen tipo, nosotros no hemos cambiado el tipo de la variable 'a' (simplemente hemos referenciado la variable a un objeto de un tipo diferente).
Los objetos, por el contrario, saben de que tipo son. El objeto entero '3', por ejemplo, contendrá el valor '3, más una designación que le dice a Python que el objeto es un entero.
[C] Recolector de basura en los objetos
En los ejemplos anteriores hemos asignado la variable 'a' a diferentes tipos de objetos en cada asginamiento. Pero cuando reasignamos una variable, ¿qué ocurre con el valor al que estaba antes referenciado?.
La respuesta es que en Python, cuando un nombre es asignado a un nuevo objeto, el espacio que tenia el primer objeto es reclamado (si este no es referenciado por ningún otro nombre u objeto). Esta reclamación automática del espacio del objeto es conocida con recolección de basura.
[D] Referencias compartidas
Introduzcamos otra variable en nuestra interacción, y veamos que ocurre con sus nombre y objetos:
>>> a = 3
>>> b = a
A esto se le llama una "referencia compartida" en Python (multiples nombres referencian al mismo objeto).

Ahora supongamos que añadimos una nueva sentencia:
>>> a = 3
>>> b = a
>>> a = 'spam'
Esta nueva sentencia simplemente crea un nuevo objeto para representar el valor de la cadena 'spam' y establece 'a' para referenciar este nuevo objeto. Sin embargo, no cambia el valor de 'b'; 'b' aun referencia al objeto original, el entero '3'.

Ahora veamos como se comportan las listas, las cuales soportan asignamientos in-place (no son inmutables), y que son simplemente colecciones de otros objetos, codificados entre corchetes:
>>> L1 = [2, 3, 4]
>>> L2 = L1
>>> L1 = 24
'L1' es una lista que contiene los objetos '2', '3' y '4'. A los elementos de una lista se accede mediante su posición. 'L1' y 'L2' referencian al mismo objeto. Con la tercera sentencia 'L1' se referencia a otro objeto; 'L2' aun referencia a la lista original.
Si cambiamos esta sentencia por esta otra, la cosa cambia:
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[24, 3, 4]
Aquí no hemos cambiado L1; hemos cambiado un componente del objeto que referencia 'L1'. Debido a que el objeto lista es compartido por otras variables, este cambio no solo afecta a 'L1' sino tambien a 'L2'
Si no quieres que esto ocurra, puedes copiar objetos, en vez de crear referencias. Hay una variedad de maneras de copiar una lista (funcion 'list', modulo 'copy' o el "slice").
Hay dos maneras de comprobar la igualdad en un programa en Python:
>>> L = [1, 2, 3]
>>> M = L
>>> L == M
True
>>> L is M
True
La primera técnica, el operador '==', comprueba si los dos objetos referenciados tienen el mismo valor. El segundo método, el operador 'is', en vez de comprobar la identidad del objeto, devuelve 'True' solo si ambos nombres apuntan al mismo objeto:
>>> L = [1, 2, 3]
>>> M = [1, 2, 3]
>>> L == M
True
>>> L is M
False
Mira que ocurre cuando realizamos las mismas operaciones sobre números pequeños:
>>> X = 42
>>> Y = 42
>>> X == Y
True
>>> X is Y
True
X e Y no son el mismo objeto ('is'), pero debido a que los enteros pequeños y las cadenas son puestos en cache y reusados, 'is' nos dice que referencian al mismo objeto.