¿No son inmutables las cadenas de Python? Entonces, ¿por qué funciona a + “” + b?


109

Mi entendimiento fue que las cadenas de Python son inmutables.

Probé el siguiente código:

a = "Dog"
b = "eats"
c = "treats"

print a, b, c
# Dog eats treats

print a + " " + b + " " + c
# Dog eats treats

print a
# Dog

a = a + " " + b + " " + c
print a
# Dog eats treats
# !!!

¿No debería Python haber evitado la asignación? Probablemente me esté perdiendo algo.

¿Alguna idea?


55
La cadena en sí es inmutable pero la etiqueta puede cambiar.
mitch

6
Asignar un nuevo valor a una variable existente es perfectamente válido. Python no tiene constantes. Esto es independiente de la mutabilidad del tipo de datos.
Felix Kling

14
Es posible que desee echar un vistazo a la id()función. atendrá una identificación diferente antes y después de la asignación, lo que indica que está apuntando a diferentes objetos. Del mismo modo, con un código como, b = alo encontrará ay btendrá la misma identificación, lo que indica que están haciendo referencia al mismo objeto.
DRH


El enlace de Delnan es exactamente a lo que me refería.
mitch

Respuestas:


180

Primero aseñaló la cadena "Perro". Luego cambió la variable apara que apunte a una nueva cadena "El perro come golosinas". En realidad, no mutó la cadena "Perro". Las cadenas son inmutables, las variables pueden apuntar a lo que quieran.


34
Es incluso más convincente intentar algo como x = 'abc'; x [1] = 'x' en Python repl
xpmatteo

1
Si desea comprender un poco más los aspectos internos, vea mi respuesta. stackoverflow.com/a/40702094/117471
Bruno Bronosky

53

Los propios objetos de cuerda son inmutables.

La variable, aque apunta a la cadena, es mutable.

Considerar:

a = "Foo"
# a now points to "Foo"
b = a
# b points to the same "Foo" that a points to
a = a + a
# a points to the new string "FooFoo", but b still points to the old "Foo"

print a
print b
# Outputs:

# FooFoo
# Foo

# Observe that b hasn't changed, even though a has.

@jason prueba el mismo tipo de operaciones con listas (que son mutables) para ver la diferencia a.append (3) corresponde a a = a + "Foo"
jimifiki

1
@jimifiki a.append(3) no es lo mismo que a = a + 3. Ni siquiera a += 3(en lugar de la adición es equivalente a .extend, no a .append).

@delnan y entonces, ¿qué? Para mostrar que las cadenas y las listas se comportan de manera diferente, puede asumir que a = a + "Foo" es lo mismo que a.append (algo). En cualquier caso, no es lo mismo. Obviamente. ¿Estabas más feliz leyendo a.extend ([algo]) en lugar de a.append (algo)? No veo esa gran diferencia en este contexto. Pero probablemente me estoy perdiendo algo. La verdad depende del contexto
jimifiki

@jimifiki: ¿De qué estás hablando? +se comporta de la misma manera para listas y cadenas: concatena creando una nueva copia y sin mutar ninguno de los operandos.

6
El punto verdaderamente importante que se debe extraer de todo esto es que las cadenas no tienen una append función porque son inmutables.
Lily Chung

46

La variable a apunta al objeto "Perro". Es mejor pensar en la variable en Python como una etiqueta. Puede mover la etiqueta a diferentes objetos, que es lo que hizo cuando cambió a = "dog"a a = "dog eats treats".

Sin embargo, la inmutabilidad se refiere al objeto, no a la etiqueta.


Si intentaras a[1] = 'z'convertirte "dog"en"dzg" , obtendrías el error:

TypeError: 'str' object does not support item assignment" 

debido a que las cadenas no admiten la asignación de elementos, son inmutables.


19

Algo es mutable solo cuando podemos cambiar los valores contenidos en la ubicación de la memoria sin cambiar la ubicación de la memoria en sí.

El truco es: si encuentra que la ubicación de la memoria antes y después del cambio es la misma, es mutable.

Por ejemplo, la lista es mutable. ¿Cómo?

>> a = ['hello']
>> id(a)
139767295067632

# Now let's modify
#1
>> a[0] = "hello new"
>> a
['hello new']
Now that we have changed "a", let's see the location of a
>> id(a)
139767295067632
so it is the same as before. So we mutated a. So list is mutable.

Una cuerda es inmutable. ¿Cómo lo probamos?

> a = "hello"
> a[0]
'h'
# Now let's modify it
> a[0] = 'n'
----------------------------------------------------------------------

obtenemos

TypeError: el objeto 'str' no admite la asignación de elementos

Entonces fallamos al mutar la cadena. Significa que una cuerda es inmutable.

Al reasignar, cambia la variable para que apunte a una nueva ubicación. Aquí no ha mutado la cadena, sino la propia variable. Lo siguiente es lo que está haciendo.

>> a = "hello"
>> id(a)
139767308749440
>> a ="world"
>> id(a)
139767293625808

idantes y después de la reasignación es diferente, por lo que esto demuestra que en realidad no está mutando, sino que apunta la variable a una nueva ubicación. Lo cual no está mutando esa cadena, sino esa variable.


11

Una variable es solo una etiqueta que apunta a un objeto. El objeto es inmutable, pero puede hacer que la etiqueta apunte a un objeto completamente diferente si lo desea.


8

Considerar:

>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='qwer'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x109198490>

Observe que la ubicación de la memoria hexadecimal no cambió cuando almacené el mismo valor en la variable dos veces. Cambió cuando almacené un valor diferente. La cuerda es inmutable. No por fanatismo, sino porque paga la penalización de rendimiento de crear un nuevo objeto en la memoria. La variable aes solo una etiqueta que apunta a esa dirección de memoria. Puede modificarse para señalar cualquier cosa.


7

La declaración a = a + " " + b + " " + cse puede desglosar en función de las sugerencias.

a + " "dice: dame los apuntos a, que no se pueden cambiar, y agrega" " a mi conjunto de trabajo actual.

memoria:

working_set = "Dog "
a = "Dog" 
b = "eats"
c = "treats"

+ bdice dame a qué bapunta, que no se puede cambiar, y agrégalo al conjunto de trabajo actual.

memoria:

working_set = "Dog eats"
a = "Dog" 
b = "eats"
c = "treats"

+ " " + cdice agregar " "al conjunto actual. Luego, dame a qué capunta, que no se puede cambiar, y agrégalo al conjunto de trabajo actual. memoria:

working_set = "Dog eats treats"
a = "Dog" 
b = "eats"
c = "treats"

Finalmente, a =dice establecer mi puntero para que apunte al conjunto resultante.

memoria:

a = "Dog eats treats"
b = "eats"
c = "treats"

"Dog"se recupera, porque no más punteros se conectan a su parte de la memoria. Nunca modificamos la sección de memoria "Dog"en la que residía, que es lo que se entiende por inmutable. Sin embargo, podemos cambiar qué etiquetas, si las hay, apuntan a esa sección de la memoria.


6
l = [1,2,3]
print id(l)
l.append(4)
print id(l) #object l is the same

a = "dog"
print id(a)
a = "cat"
print id(a) #object a is a new object, previous one is deleted

5

Existe una diferencia entre los datos y la etiqueta a la que están asociados. Por ejemplo cuando lo haces

a = "dog"

los datos "dog"se crean y se colocan bajo la etiqueta a. La etiqueta puede cambiar, pero lo que está en la memoria no lo hará. Los datos "dog"seguirán existiendo en la memoria (hasta que el recolector de basura los elimine) después de que lo haga

a = "cat"

En su programa aahora ^ apunta a ^ "cat"pero la cadena "dog"no ha cambiado.


3

Las cadenas de Python son inmutables. Sin embargo, ano es una cadena: es una variable con un valor de cadena. No puede mutar la cadena, pero puede cambiar el valor de la variable a una nueva cadena.


2

Las variables pueden apuntar a cualquier lugar que deseen. Se producirá un error si hace lo siguiente:

a = "dog"
print a                   #dog
a[1] = "g"                #ERROR!!!!!! STRINGS ARE IMMUTABLE

2

Los objetos de cadena de Python son inmutables. Ejemplo:

>>> a = 'tanim'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281536'
>>> a = 'ahmed'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281600'

En este ejemplo podemos ver que cuando asignamos un valor diferente en un no se modifica, se crea un nuevo objeto.
Y no se puede modificar. Ejemplo:

  >>> a[0] = 'c'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    **TypeError**: 'str' object does not support item assignment

Ocurre un error.


2

'mutable' significa que podemos cambiar el contenido de la cadena, 'inmutable' significa que no podemos agregar una cadena adicional.

haga clic para una prueba de foto


1

>>> a = 'dogs'

>>> a.replace('dogs', 'dogs eat treats')

'dogs eat treats'

>>> print a

'dogs'

Inmutable, ¿no es así?

La parte de cambio de variable ya se ha discutido.


1
Esto no prueba ni refuta la mutabilidad de las cadenas de Python, solo que el replace()método devuelve una nueva cadena.
Brent Hronik

1

Considere esta adición a su ejemplo

 a = "Dog"
 b = "eats"
 c = "treats"
 print (a,b,c)
 #Dog eats treats
 d = a + " " + b + " " + c
 print (a)
 #Dog
 print (d)
 #Dog eats treats

Una de las explicaciones más precisas que encontré en un blog es:

En Python, (casi) todo es un objeto. Lo que comúnmente llamamos "variables" en Python son nombres más propios. Asimismo, "asignación" es en realidad la vinculación de un nombre a un objeto. Cada enlace tiene un alcance que define su visibilidad, generalmente el bloque en el que se origina el nombre.

P.ej:

some_guy = 'Fred'
# ...
some_guy = 'George'

Cuando más tarde decimos some_guy = 'George', el objeto de cadena que contiene 'Fred' no se ve afectado. Acabamos de cambiar el enlace del nombre some_guy. Sin embargo, no hemos cambiado los objetos de cadena 'Fred' o 'George'. En lo que a nosotros respecta, es posible que vivan indefinidamente.

Enlace al blog: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/


1

Agregando un poco más a las respuestas mencionadas anteriormente.

id de una variable cambia con la reasignación.

>>> a = 'initial_string'
>>> id(a)
139982120425648
>>> a = 'new_string'
>>> id(a)
139982120425776

Lo que significa que hemos mutado la variable apara que apunte a una nueva cadena. Ahora existen dos string (str) objetos:

'initial_string' con id = 139982120425648

y

'new_string'con id= 139982120425776

Considere el siguiente código:

>>> b = 'intitial_string'
>>> id(b)
139982120425648

Ahora, bapunta al 'initial_string'y tiene lo mismo idque atenía antes de la reasignación.

Por lo tanto, 'intial_string'no se ha mutado.


0

Resumiendo:

a = 3
b = a
a = 3+2
print b
# 5

No inmutable:

a = 'OOP'
b = a
a = 'p'+a
print b
# OOP

Inmutable:

a = [1,2,3]
b = range(len(a))
for i in range(len(a)):
    b[i] = a[i]+1

Este es un error en Python 3 porque es inmutable. Y no es un error en Python 2 porque claramente no es inmutable.


0

La función incorporada id()devuelve la identidad de un objeto como un número entero. Este número entero generalmente corresponde a la ubicación del objeto en la memoria.

\>>a='dog'
\>>print(id(a))

139831803293008

\>>a=a+'cat'
\>>print(id(a))

139831803293120

Inicialmente, 'a' se almacena en la ubicación de memoria 139831803293008, ya que el objeto de cadena es inmutable en Python si intenta modificar y reasignar la referencia se eliminará y será un puntero a una nueva ubicación de memoria (139831803293120).


0
a = 'dog'
address = id(a)
print(id(a))

a = a + 'cat'
print(id(a))      #Address changes

import ctypes
ctypes.cast(address, ctypes.py_object).value    #value at old address is intact

2
Si bien este código puede resolver el problema del OP, es mejor incluir una explicación de cómo su código aborda el problema del OP. De esta manera, los futuros visitantes pueden aprender de su publicación y aplicarla a su propio código. SO no es un servicio de codificación, sino un recurso de conocimiento. Además, es más probable que se voten a favor las respuestas completas y de alta calidad. Estas características, junto con el requisito de que todas las publicaciones sean independientes, son algunas de las fortalezas de SO como plataforma, que la diferencia de los foros. Puede editar para agregar información adicional y / o complementar sus explicaciones con documentación fuente
SherylHohman


-1

Simplemente concatenamos los dos valores de cadena. Nunca cambiamos el valor de (a). Justo ahora (a) representa otro bloque de memoria que tiene el valor "dogdog". Porque en el backend, una variable nunca representa dos bloques de memoria al mismo tiempo. El valor de (a) antes de la concatenación era "perro". Pero después de eso (a) representa el "perro-perro", porque ahora (a) en el representante de backend. el bloque que tiene el valor "dogdog". Y "perro" es rep. por (b) y "perro" no se cuenta como valor de basura hasta que (b) represente el "perro".

La confusión es que representamos los bloques de memoria (que contienen datos o información) en el backend con el mismo nombre de variable.


-2

Puede hacer que una matriz numpy sea inmutable y usar el primer elemento:

numpyarrayname[0] = "write once"

luego:

numpyarrayname.setflags(write=False)

o

numpyarrayname.flags.writeable = False
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.