¿Por qué las tuplas pueden contener elementos mutables?

Si una tupla es inmutable, ¿por qué puede contener elementos mutables?

Aparentemente es una contradicción que cuando se modifica un elemento mutable, como una lista, la tupla a la que pertenece se mantenga inmutable.

Esa es una excelente pregunta.

La idea clave es que las tuplas no tienen forma de saber si los objetos dentro de ellas son mutables. Lo único que hace que un objeto sea mutable es tener un método que modifique sus datos. En general, no hay manera de detectar esto.

Otra idea es que los contenedores de Python en realidad no contienen nada. En cambio, mantienen referencias a otros objetos. Del mismo modo, las variables de Python no son como variables en lenguajes comstackdos; en cambio, los nombres de las variables son solo claves en un diccionario de espacio de nombres donde están asociados con un objeto correspondiente. Ned Batchhelder explica esto muy bien en su blog . De cualquier manera, los objetos solo conocen su recuento de referencias; no saben qué son esas referencias (variables, contenedores o las partes internas de Python).

Juntos, estos dos puntos de vista explican su misterio (por qué una tupla inmutable que “contiene” una lista parece cambiar cuando cambia la lista subyacente). De hecho, la tupla no cambió (todavía tiene las mismas referencias a otros objetos que antes). La tupla no pudo cambiar (porque no tenía métodos de mutación). Cuando la lista cambió, la tupla no recibió notificación del cambio (la lista no sabe si una variable, una tupla u otra lista hacen referencia a ella).

Mientras estamos en el tema, aquí hay algunos otros pensamientos para ayudar a completar su modelo mental de qué son las tuplas, cómo funcionan y su uso previsto:

  1. Las tuplas se caracterizan menos por su inmutabilidad y más por su propósito previsto.
    Las tuplas son la forma de Python de recostackr información heterogénea bajo un mismo techo. Por ejemplo, s = ('www.python.org', 80) reúne una cadena y un número para que el par host / puerto se pueda pasar como un zócalo, un objeto compuesto. Visto desde ese punto de vista, es perfectamente razonable tener componentes mutables.

  2. La inmutabilidad va de la mano con otra propiedad, la hashability . Pero la hashabilidad no es una propiedad absoluta. Si uno de los componentes de la tupla no es hashable, entonces la tupla general tampoco es hashable. Por ejemplo, t = ('red', [10, 20, 30]) no es hashable.

El último ejemplo muestra una tupla que contiene una cadena y una lista. La tupla en sí no es mutable (es decir, no tiene ningún método para cambiar su contenido). Del mismo modo, la cadena es inmutable porque las cadenas no tienen ningún método de mutación. El objeto de lista tiene métodos de mutación, por lo que se puede cambiar. Esto muestra que la mutabilidad es una propiedad de un tipo de objeto: algunos objetos tienen métodos de mutación y otros no. Esto no cambia solo porque los objetos están nesteds.

Recuerda dos cosas. Primero, la inmutabilidad no es magia, es simplemente la ausencia de métodos de mutación. En segundo lugar, los objetos no saben qué variables o contenedores se refieren a ellos, solo conocen el recuento de referencias.

Espero que esto te haya sido útil 🙂

Eso es porque las tuplas no contienen listas, cadenas o números. Contienen referencias a otros objetos . 1 La incapacidad de cambiar la secuencia de referencias que contiene una tupla no significa que no pueda mutar los objetos asociados con esas referencias. 2

1. Objetos, valores y tipos (ver: segundo al último párrafo)
2. La jerarquía de tipos estándar (ver: “Secuencias inmutables”)

En primer lugar, la palabra “inmutable” puede significar muchas cosas diferentes para diferentes personas. Me gusta particularmente cómo Eric Lippert categorizó la inmutabilidad en su publicación de blog . Allí, él enumera estos tipos de inmutabilidad:

  • Inmutabilidad real-trulio
  • Escritura una vez inmutabilidad.
  • Paleta de inmutabilidad
  • Inmutabilidad superficial vs profunda
  • Fachadas inmutables
  • Inmutabilidad observacional

Estos pueden combinarse de varias maneras para hacer aún más tipos de inmutabilidad, y estoy seguro de que existen más. El tipo de inmutabilidad en el que parece interesado la inmutabilidad profunda (también conocida como transitiva), en la que los objetos inmutables solo pueden contener otros objetos inmutables.

El punto clave de esto es que la inmutabilidad profunda es solo uno de los muchos tipos de inmutabilidad. Puede adoptar el tipo que prefiera, siempre que sea consciente de que su noción de “inmutable” probablemente difiera de la noción de “inmutable” de otra persona.

Según tengo entendido, esta pregunta debe reformularse como una pregunta sobre las decisiones de diseño: ¿Por qué los diseñadores de Python eligieron crear un tipo de secuencia inmutable que puede contener objetos mutables?

Para responder a esta pregunta, tenemos que pensar en el propósito que sirven las tuplas : sirven como secuencias rápidas de propósito general . Con eso en mente, se vuelve bastante obvio por qué las tuplas son inmutables pero pueden contener objetos mutables. Esto es:

  1. Las tuplas son rápidas y eficientes en la memoria: las tuplas son más rápidas de crear que las listas porque son inmutables. La inmutabilidad significa que las tuplas se pueden crear como constantes y cargar como tales, utilizando el plegado constante . También significa que son más rápidos y más eficientes en la memoria para crear porque no hay necesidad de una ubicación general, etc. Son un poco más lentos que las listas para el acceso aleatorio a los elementos, pero de nuevo más rápido para desempacar (al menos en mi máquina). Si las tuplas fueran mutables, entonces no serían tan rápidas para propósitos como estos.

  2. Las tuplas son de uso general : las tuplas deben poder contener cualquier tipo de objeto. Se utilizan para (rápidamente) hacer cosas como listas de argumentos de longitud variable (a través del operador * en las definiciones de funciones). Si las tuplas no pudieran contener objetos mutables, serían inútiles para cosas como esta. Python tendría que usar listas, lo que probablemente ralentizaría las cosas, y ciertamente sería menos eficiente en memoria.

Así que, para cumplir su propósito, las tuplas deben ser inmutables, pero también deben poder contener objetos mutables. Si los diseñadores de Python quisieran crear un objeto inmutable que garantice que todos los objetos que “contiene” también son inmutables, tendrían que crear un tercer tipo de secuencia. La ganancia no merece la complejidad extra.

No puedes cambiar el id de sus elementos. Así que siempre contendrá los mismos artículos.

 $ python >>> t = (1, [2, 3]) >>> id(t[1]) 12371368 >>> t[1].append(4) >>> id(t[1]) 12371368 

Me arriesgaré aquí y diré que la parte relevante aquí es que si bien puede cambiar el contenido de una lista, o el estado de un objeto, contenido dentro de una tupla, lo que no puede cambiar es que el objeto o lista está ahí. Si tuvieras algo que dependiera de que la cosa [3] fuera una lista, incluso si estaba vacía, entonces podría ser útil.

Una tupla es inmutable en el sentido de que la tupla en sí no puede expandirse o encogerse, no es que todos los elementos contenidos sean inmutables. De lo contrario, las tuplas son aburridas.

Una de las razones es que no hay una forma general en Python para convertir un tipo mutable en uno inmutable (consulte el PEP 351 rechazado y la discusión vinculada sobre por qué se rechazó). Por lo tanto, sería imposible colocar varios tipos de objetos en tuplas si tuviera esta restricción, incluyendo casi cualquier objeto no hashable creado por el usuario.

La única razón por la que los diccionarios y los conjuntos tienen esta restricción es que requieren que los objetos sean hashable, ya que se implementan internamente como tablas hash. Pero tenga en cuenta que, irónicamente, los diccionarios y los conjuntos en sí mismos no son inmutables (o hashable). Las tuplas no utilizan el hash de un objeto, por lo que su mutabilidad no importa.