tipos mutables inmutables inmutable inmutabilidad ejemplos datos python list tuples immutability mutable

python - mutables - un tipo mutable dentro de un contenedor inmutable



mutable e inmutable (3)

Estoy un poco confundido sobre la modificación de los miembros de la tupla. Lo siguiente no funciona:

>>> thing = ([''a''],) >>> thing[0] = [''b''] TypeError: ''tuple'' object does not support item assignment >>> thing ([''a''],)

Pero esto funciona:

>>> thing[0][0] = ''b'' >>> thing ([''b''],)

También funciona:

>>> thing[0].append(''c'') >>> thing ([''b'', ''c''],)

No funciona, y funciona (¿eh ?!):

>>> thing[0] += ''d'' TypeError: ''tuple'' object does not support item assignment >>> thing ([''b'', ''c'', ''d''],)

Aparentemente equivalente a la anterior, pero funciona:

>>> e = thing[0] >>> e += ''e'' >>> thing ([''b'', ''c'', ''d'', ''e''],)

Entonces, ¿cuáles son exactamente las reglas del juego, cuando puedes y no puedes modificar algo dentro de una tupla? Parece ser más como la prohibición de usar el operador de asignación para los miembros de la tupla, pero los dos últimos casos me confunden.


No puede modificar la tupla, pero puede modificar el contenido de las cosas contenidas dentro de la tupla. Las listas (junto con los conjuntos, los dictados y los objetos) son un tipo de referencia y, por lo tanto, la "cosa" en la tupla es solo una referencia: la lista real es un objeto mutable al que apunta esa referencia y puede modificarse sin cambiar el referencia en sí.

( + ,) <--- your tuple (this can''t be changed) | | v [''a''] <--- the list object your tuple references (this can be changed)

Después de thing[0][0] = ''b'' :

( + ,) <--- notice how the contents of this are still the same | | v [''b''] <--- but the contents of this have changed

Después de la thing[0].append(''c'') :

( + ,) <--- notice how this is still the same | | v [''b'',''c''] <--- but this has changed again

La razón por la cual += errors es que no es completamente equivalente a .append() - realmente hace una adición y luego una asignación (y la asignación falla), en lugar de simplemente anexarse ​​en el lugar.


No puede reemplazar un elemento de una tupla, pero puede reemplazar todo el contenido del elemento. Esto funcionará:

thing[0][:] = [''b'']


Siempre puede modificar un valor mutable dentro de una tupla. El comportamiento desconcertante que ves con

>>> thing[0] += ''d''

es causado por += . El operador += realiza una adición in situ pero también una asignación : la adición in situ funciona solo en el archivo, pero la asignación falla, ya que la tupla es inmutable. Pensando en eso

>>> thing[0] = thing[0] + ''d''

lo explica mejor Podemos usar el módulo dis de la biblioteca estándar para ver el bytecode generado de ambas expresiones. Con += obtenemos un bytecode INPLACE_ADD :

>>> def f(some_list): ... some_list += ["foo"] ... >>> dis.dis(f) 2 0 LOAD_FAST 0 (some_list) 3 LOAD_CONST 1 (''foo'') 6 BUILD_LIST 1 9 INPLACE_ADD 10 STORE_FAST 0 (some_list) 13 LOAD_CONST 0 (None) 16 RETURN_VALUE

Con + obtenemos un BINARY_ADD :

>>> def g(some_list): ... some_list = some_list + ["foo"] >>> dis.dis(g) 2 0 LOAD_FAST 0 (some_list) 3 LOAD_CONST 1 (''foo'') 6 BUILD_LIST 1 9 BINARY_ADD 10 STORE_FAST 0 (some_list) 13 LOAD_CONST 0 (None) 16 RETURN_VALUE

Tenga en cuenta que obtenemos un STORE_FAST en ambos lugares. Este es el bytecode que falla cuando intentas almacenar de nuevo en una tupla: el INPLACE_ADD que viene justo antes funciona bien.

Esto explica por qué el caso "No funciona y funciona" deja atrás la lista modificada: la tupla ya tiene una referencia a la lista:

>>> id(thing[0]) 3074072428L

La lista luego es modificada por INPLACE_ADD y STORE_FAST falla:

>>> thing[0] += ''d'' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: ''tuple'' object does not support item assignment

Entonces la tupla todavía tiene una referencia a la misma lista, pero la lista ha sido modificada in situ:

>>> id(thing[0]) 3074072428L >>> thing[0] [''b'', ''c'', ''d'']