Objetivo: comprender los conceptos críticos de mutabilidad, inmutabilidad y referencia en Python.
Estás ante un artículo esencial en tu formación como programador en Python. Te llevará sólo cinco minutos leerlo, pero puede dar luz sobre conceptos que no resultan ni mucho menos obvios cuando se comienza con Python, particularmente si procedes de otros lenguajes de programación.
Por lo general, cuando empezamos a estudiar un lenguaje de programación, se nos cuenta que existen distintos tipos de datos, tales como numéricos, strings, etc.
En Python se nos enseña algo más: determinados tipos son inmutables y otros mutables. Los números, los strings y las tuplas son inmutables. Por el contrario, las listas son mutables.
Ahí ya empiezan a sacudirnos algo; pero nuestro cerebro, al que no le gustan las dudas, intenta buscar una respuesta coherente para no quedarse atascado y seguir avanzando. Claro: los números, los strings y las tuplas no se pueden modificar, mientras que las listas sí.
Bueno, bueno, un momento:
>>> a = 5
>>> b = 'pradera'
La variable a contiene un número; la variable b contiene un string. Hemos dicho que los números y los strings son inmutables. ¿Significa eso que no podemos modificar ni a ni b?
>>> a = 6
>>> b = 'casa'
>>> a
6
>>> b
'casa'
Pues sí, se pueden modificar (de no ser así Python tendría un serio problema), por lo que eso de la inmutabilidad debe de significar algo diferente. Decir que un tupla es inmutable, pero una lista no, ¿significará acaso que ésta tiene cosquillas pero la otra no?
Más adelante cuando estudiamos las listas y las tuplas creemos haber comprendido, al fin, el misterio.
>>> lista = [1, 2, 3, 4]
>>> lista[0] = 5
>>> lista
[5, 2, 3, 4]
Bien: las listas, mutables, se pueden modificar.
>>> tupla = (1, 2, 3, 4)
>>> tupla[0] = 5
Traceback (most recent call last):
File "<pyshell#55>", line 1, in <module>
tupla[0] = 5
TypeError: 'tuple' object does not support item assignment
En cambio, las tuplas, inmutables, no, como podemos comprobar.
Aprendemos también que los strings también son secuencias, como las listas y las tuplas y, como tales, podemos acceder a sus caracteres individuales:
>>> serie = 'la casa de la pradera'
>>> serie[3]
'c'
Pero no podemos modificarlos:
>>> serie[3] = 'k'
Traceback (most recent call last):
File "<pyshell#60>", line 1, in <module>
serie[3] = 'k'
TypeError: 'str' object does not support item assignment
Un error semejante al que recibimos al tratar de modificar una tupla. Esto hace que cuadre todo: los strings son inmutables, también.
Pero ¿cuadra realmente todo? Parece que sí, aunque no aparenta tener mucho sentido práctico eso de la inmutabilidad de los números.
Tarde o temprano, cualquier estudiante de Python que ya ha programado en otros lenguajes se enfrenta al siguiente misterio.
Observémoslo muy despacio:
>>> x = 5
>>> y = x
Pregunta: ¿cuánto crees que vale y?
En efecto, 5:
>>> y
5
Modifiquemos ahora el valor de x:
>>> x = 6
¿Cuánto crees que valdrá ahora y?
Vamos a ver, no me asustes: x e y son dos variables diferentes, cada una con su propio contenido; por lo tanto y tiene que seguir valiendo 5, aunque hayas modificado x, ¿no?
En efecto, así es:
>>> y
5
Veamos otro ejemplo. Ahora x es una lista:
>>> x = [1, 2, 3, 4]
>>> y = x
>>> y
[1, 2, 3, 4]
Voy a modificar el valor de x:
>>> x[0] = 5
>>> x
[5, 2, 3, 4]
¿Cuánto crees que valdrá ahora y?
Por la lógica anterior, x e y son dos variables diferentes, cada una con su propio contenido, por lo que cabría esperar que el valor de y fuera [1, 2, 3, 4].
Pero no es así:
>>> y
[5, 2, 3, 4]
Esto rompe completamente nuestros esquemas. Si somos programadores en otros lenguajes una alarma nos sacude inmediatamente.
¿Por qué al modificar x ha cambiado y también?
De acuerdo: los números son inmutables, pero las listas no. ¿Pero qué tiene que ver esto con el hecho de que se haya modificado el valor de y?
Para resolver el misterio hace falta un una nueva pieza clave: el concepto de referencia.
Veamos lo que está sucediendo realmente entre bastidores:
>>> x = 5
En Python, todo son objetos. Se acaba de crear uno nuevo: un número de valor 5.
Este objeto, que reside en algún lugar de la memoria, posee un identificador único que lo diferencia del resto de los objetos. Piensa en él como si fuera su DNI (es, en realidad, la dirección de memoria en la que reside el objeto).
Para conocer ese identificador, Python dispone de la función id():
>>> id(5)
137396064
Y ahora el concepto crítico: la variable x no es más que una referencia a ese objeto. A efectos prácticos, es como si se tratara de un puntero (empleando conceptos de otros lenguajes) a ese objeto. No contiene su valor, sino una referencia a él.
La función id(), aplicada a una variable, nos devuelve el id del objeto al que referencia, por lo que obtenemos el mismo valor que antes:
>>> id(x)
137396064
Proseguimos; asignamos a la variable y el valor de x:
>>> y = x
Observa ahora cuidadosamente qué pasa al preguntar por el id del objeto al que referencia y:
>>> id(y)
137396064
Exactamente el mismo al que referencia x. Tanto x como y apuntan al mismo objeto.
Este es un concepto radicalmente distinto al de las variables en otros lenguajes, en los que el contenido de x y de y estaría en diferentes direcciones de memoria.
A continuación, modificamos x:
>>> x = 6
>>> id(x)
137396080
Se ha creado un nuevo objeto, de valor 6, y ahora x apunta a ese valor. Esto también es muy diferente a lo que sucedería en otros lenguajes: la variable x seguiría en la misma dirección de memoria, habiendo modificado únicamente su contenido.
Fijémonos que, como es de esperar, y sigue apuntando al objeto primero:
>>> id(y)
137396064
>>> y
5
Analicemos ahora qué sucede en el caso de las listas:
>>> x = [1, 2, 3, 4]
>>> y = x
>>> id(x)
170964396
>>> id(y)
170964396
De momento, todo es exactamente igual que antes: se ha creado un único objeto, la lista [1, 2, 3, 4]
, que está referenciado por dos variables, x e y.
Si ahora modificamos directamente el objeto (al ser mutable, podemos hacerlo):
>>> x[0] = 5
>>> id(x)
170964396
>>> id(y)
170964396
comprobamos que tanto x como y siguen siendo referencias al mismo objeto.
Por lo tanto y tiene el mismo valor que x:
>>> y
[5, 2, 3, 4]
En el ejemplo anterior, cuando hicimos x = 6
, se creó un nuevo objeto. En el caso de la lista, hemos modificado in situ el propio objeto, aprovechándonos de su mutabilidad. Es como si un cirujano hubiese realizado una operación sobre el objeto.
La situación habría sido muy distinta si hubiéramos hecho esto otro:
>>> x = [1, 2, 3, 4]
>>> y = x
>>> x = [5, 2, 3, 4]
>>> y
[1, 2, 3, 4]
>>> id(x)
170489580
>>> id(y)
170791756
Fíjate en la importante diferencia: al hacer x = [5, 2, 3, 4]
hemos creado un objeto diferente, de modo que x e y apuntan a objetos distintos y por eso ahora sus valores difieren. En el caso anterior, al hacer x[0] = 5
modificamos directamente el mismo objeto y no se creó otro nuevo.
Espero que todo resulte más claro ahora y entiendas la diferencia esencial que hay en lo que respecta al concepto de variable respecto a otros lenguajes de programación. Si es así, felicítate, pues habrás cambiado de nivel en tu proceso de aprendizaje de Python.
Javier Montero Gabarró
http://elclubdelautodidacta.es/wp/2012/09/python-el-sagrado-misterio-de-la-inmutabilidad/
El texto de este artículo se encuentra sometido a una licencia Creative Commons del tipo CC-BY-NC-ND (reconocimiento, no comercial, sin obra derivada, 3.0 unported)
El Club del Autodidacta
Consulta el índice completo de artículos relacionados con Python.