¿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?

jason
fuente
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.

Diamante negro
fuente
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.
Sebastian Paaske Tørholm
fuente
@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.

chrtan
fuente
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.

Harish Kayarohanam
fuente
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.

jcollado
fuente
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.

Bruno Bronosky
fuente
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.

Spencer Rathbun
fuente
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
usuario744629
fuente
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.

mitch
fuente
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.

Michael J. Barber
fuente
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
GuruJeya
fuente
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.

Tanim_113
fuente
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


fuente
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.

Lohit Bisen
fuente
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/

7to4
fuente
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.

Rahul Madiwale
fuente
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.

pjf
fuente
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).

aprendiz5060
fuente
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
10xAI
fuente
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

Esta imagen da la respuesta. Por favor leelo.

ingrese la descripción de la imagen aquí

Wen Qi
fuente
-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.

dineshmehta
fuente
-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
PauluaP
fuente