Les chaînes Python ne sont-elles pas immuables? Alors pourquoi a + "" + b fonctionne-t-il?

110

Ma compréhension était que les chaînes Python sont immuables.

J'ai essayé le code suivant:

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
# !!!

Python n'aurait-il pas dû empêcher l'affectation? Il me manque probablement quelque chose.

Une idée?

Jason
la source
55
La chaîne elle-même est immuable mais l'étiquette peut changer.
mitch
6
Attribuer une nouvelle valeur à une variable existante est parfaitement valide. Python n'a pas de constantes. Ceci est indépendant de la mutabilité du type de données.
Felix Kling
14
Vous voudrez peut-être jeter un œil à la id()fonction. aaura un identifiant différent avant et après l'affectation, indiquant qu'il pointe vers différents objets. De même avec du code comme b = avous le trouverez aet baurez le même identifiant, indiquant qu'ils référencent le même objet.
DRH
Le lien de delnan est exactement ce à quoi je faisais référence.
mitch

Réponses:

182

Tout d'abord apointé sur la chaîne "Dog". Ensuite, vous avez changé la variable apour pointer vers une nouvelle chaîne "Dog mange des friandises". Vous n'avez pas fait muter la chaîne "Dog". Les chaînes sont immuables, les variables peuvent pointer vers ce qu'elles veulent.

Bort
la source
34
C'est encore plus convaincant d'essayer quelque chose comme x = 'abc'; x [1] = 'x' dans la
réplique
1
Si vous souhaitez comprendre un peu plus les éléments internes, consultez ma réponse. stackoverflow.com/a/40702094/117471
Bruno Bronosky
54

Les objets string eux-mêmes sont immuables.

La variable, aqui pointe vers la chaîne, est modifiable.

Considérer:

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
la source
@jason essaie le même genre d'opérations avec des listes (qui sont mutables) pour voir la différence a.append (3) correspond à a = a + "Foo"
jimifiki
1
@jimifiki a.append(3) n'est pas la même chose que a = a + 3. Ce n'est même pas a += 3(l'ajout inplace équivaut à .extend, pas à .append).
@delnan et alors, quoi? Pour montrer que les chaînes et les listes se comportent différemment, vous pouvez supposer que a = a + "Foo" est identique à a.append (quelque chose). En tout cas ce n'est pas pareil. Évidemment. Étiez-vous plus heureux de lire a.extend ([quelque chose]) au lieu de a.append (quelque chose)? Je ne vois pas cette grande différence dans ce contexte. Mais il me manque probablement quelque chose. La vérité dépend du contexte
jimifiki
@jimifiki: De quoi tu parles? +se comporte de la même manière pour les listes et les chaînes - il concatène en crée une nouvelle copie et ne mute aucun des opérandes.
6
Le point vraiment important à retenir de tout cela est que les chaînes n'ont pas de append fonction parce qu'elles sont immuables.
Lily Chung
46

La variable a pointe vers l'objet "Chien". Il est préférable de considérer la variable en Python comme une balise. Vous pouvez déplacer la balise vers différents objets, ce que vous avez fait lorsque vous êtes passé a = "dog"à a = "dog eats treats".

Cependant, l'immuabilité fait référence à l'objet, pas à la balise.


Si vous avez essayé a[1] = 'z'de faire "dog"dans "dzg", vous obtiendrez l'erreur:

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

car les chaînes ne prennent pas en charge l'attribution d'éléments, elles sont donc immuables.

Chrtan
la source
19

Quelque chose n'est modifiable que lorsque nous sommes en mesure de modifier les valeurs contenues dans l'emplacement mémoire sans changer l'emplacement mémoire lui-même.

L'astuce est la suivante: si vous trouvez que l'emplacement mémoire avant et après le changement est le même, il est modifiable.

Par exemple, la liste est modifiable. Comment?

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

Une chaîne est immuable. Comment le prouver?

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

on a

TypeError: l'objet 'str' ne prend pas en charge l'affectation d'élément

Nous n'avons donc pas réussi à muter la chaîne. Cela signifie qu'une chaîne est immuable.

Lors de la réaffectation, vous modifiez la variable pour qu'elle pointe elle-même vers un nouvel emplacement. Ici, vous n'avez pas muté la chaîne, mais muté la variable elle-même. Voici ce que vous faites.

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

idavant et après la réaffectation est différent, cela prouve donc que vous n'êtes pas en train de muter, mais que vous pointez la variable vers un nouvel emplacement. Ce qui n'est pas une mutation de cette chaîne, mais une mutation de cette variable.

Harish Kayarohanam
la source
11

Une variable n'est qu'une étiquette pointant vers un objet. L'objet est immuable, mais vous pouvez faire pointer l'étiquette vers un objet complètement différent si vous le souhaitez.

jcollado
la source
8

Considérer:

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

Notez que l'emplacement de la mémoire hexadécimale n'a pas changé lorsque j'ai stocké la même valeur dans la variable deux fois. Cela a changé lorsque j'ai stocké une valeur différente. La chaîne est immuable. Pas à cause du fanatisme, mais parce que vous payez la pénalité de performance de créer un nouvel objet en mémoire. La variable aest juste une étiquette pointant vers cette adresse mémoire. Il peut être modifié pour pointer vers n'importe quoi.

Bruno Bronosky
la source
7

L'instruction a = a + " " + b + " " + cpeut être décomposée en fonction de pointeurs.

a + " "dit donnez-moi sur quels apoints, ce qui ne peut pas être changé, et ajoutez- " "moi à mon ensemble de travail actuel.

Mémoire:

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

+ bdit donnez-moi sur quels bpoints, ce qui ne peut pas être changé, et ajoutez-le à l'ensemble de travail actuel.

Mémoire:

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

+ " " + cdit ajouter " "à l'ensemble actuel. Ensuite, donnez-moi sur quels cpoints, ce qui ne peut pas être changé, et ajoutez-le à l'ensemble de travail actuel. Mémoire:

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

Enfin, a =dit définir mon pointeur pour qu'il pointe vers l'ensemble résultant.

Mémoire:

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

"Dog"est récupéré, car plus aucun pointeur ne se connecte à son morceau de mémoire. Nous n'avons jamais modifié la section mémoire "Dog"dans laquelle résidait, c'est ce que l'on entend par immuable. Cependant, nous pouvons changer les étiquettes, le cas échéant, qui pointent vers cette section de mémoire.

Spencer Rathbun
la source
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
user744629
la source
5

Il existe une différence entre les données et l'étiquette à laquelle elles sont associées. Par exemple quand vous faites

a = "dog"

les données "dog"sont créées et placées sous l'étiquette a. L'étiquette peut changer mais ce qui est en mémoire ne le sera pas. Les données "dog"existeront toujours en mémoire (jusqu'à ce que le garbage collector les supprime) après que vous ayez fait

a = "cat"

Dans votre programme amaintenant ^ pointe vers ^ "cat"mais la chaîne "dog"n'a pas changé.

mitch
la source
3

Les chaînes Python sont immuables. Cependant, ce an'est pas une chaîne: c'est une variable avec une valeur de chaîne. Vous ne pouvez pas muter la chaîne, mais pouvez changer la valeur de la variable en une nouvelle chaîne.

Michael J. Barber
la source
2

Les variables peuvent pointer n'importe où elles le souhaitent. Une erreur sera générée si vous procédez comme suit:

a = "dog"
print a                   #dog
a[1] = "g"                #ERROR!!!!!! STRINGS ARE IMMUTABLE
GuruJeya
la source
2

Les objets chaîne Python sont immuables. Exemple:

>>> 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'

Dans cet exemple, nous pouvons voir que lorsque nous attribuons une valeur différente à un, cela ne modifie pas, un nouvel objet est créé.
Et il ne peut pas être modifié. Exemple:

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

Une erreur se produit.

Tanim_113
la source
2

'mutable' signifie que nous pouvons changer le contenu de la chaîne, 'immutable' signifie que nous ne pouvons pas ajouter de chaîne supplémentaire.

cliquez pour une preuve photo


la source
1

>>> a = 'dogs'

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

'dogs eat treats'

>>> print a

'dogs'

Immuable, n'est-ce pas?!

La partie de changement de variable a déjà été discutée.

Lohit Bisen
la source
1
Cela ne prouve ni ne réfute la mutabilité des chaînes python, mais simplement que la replace()méthode renvoie une nouvelle chaîne.
Brent Hronik
1

Considérez cet ajout à votre exemple

 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

L'une des explications les plus précises que j'ai trouvées dans un blog est:

En Python, (presque) tout est un objet. Ce que nous appelons communément des «variables» en Python sont plus correctement appelés noms. De même, «affectation» est en réalité la liaison d'un nom à un objet. Chaque liaison a une portée qui définit sa visibilité, généralement le bloc dont le nom provient.

Par exemple:

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

Lorsque nous disons plus tard some_guy = 'George', l'objet chaîne contenant 'Fred' n'est pas affecté. Nous venons de changer la liaison du nom some_guy. Cependant, nous n'avons pas changé les objets de chaîne 'Fred' ou 'George'. En ce qui nous concerne, ils peuvent vivre indéfiniment.

Lien vers le blog: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

7to4
la source
1

Ajouter un peu plus aux réponses mentionnées ci-dessus.

id d'une variable change lors de la réaffectation.

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

Ce qui signifie que nous avons muté la variable apour qu'elle pointe vers une nouvelle chaîne. Il existe maintenant deux string objets (str):

'initial_string'avec id= 139982120425648

et

'new_string'avec id= 139982120425776

Considérez le code ci-dessous:

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

Maintenant, bpointe vers le 'initial_string'et a le même idque celui aavant la réaffectation.

Ainsi, le 'intial_string'n'a pas été muté.

Rahul Madiwale
la source
0

En résumé:

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

Pas immuable:

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

Immuable:

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

Il s'agit d'une erreur dans Python 3 car elle est immuable. Et pas une erreur dans Python 2 car clairement ce n'est pas immuable.

pjf
la source
0

La fonction intégrée id()renvoie l'identité d'un objet sous forme d'entier. Cet entier correspond généralement à l'emplacement de l'objet en mémoire.

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

139831803293008

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

139831803293120

Initialement, 'a' est stocké dans l'emplacement mémoire 139831803293008, car l'objet chaîne est immuable en python si vous essayez de modifier et de réaffecter la référence sera supprimée et sera un pointeur vers un nouvel emplacement mémoire (139831803293120).

apprenant5060
la source
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
la source
2
Bien que ce code puisse résoudre le problème du PO, il est préférable d'inclure une explication sur la façon dont votre code résout le problème du PO. De cette manière, les futurs visiteurs peuvent apprendre de votre publication et l'appliquer à leur propre code. SO n'est pas un service de codage, mais une ressource de connaissances. En outre, les réponses complètes et de haute qualité sont plus susceptibles d'être votées. Ces fonctionnalités, ainsi que l'exigence que tous les messages soient autonomes, sont quelques-unes des forces de SO en tant que plate-forme, qui le différencie des forums. Vous pouvez modifier pour ajouter des informations supplémentaires et / ou pour compléter vos explications avec la documentation source
SherylHohman
-1

Cette image donne la réponse. Lisez-le s'il vous plaît.

entrez la description de l'image ici

Wen Qi
la source
-1

Nous concaténons simplement les deux valeurs de chaîne. Nous ne changeons jamais la valeur de (a). Tout à l'heure (a) représente un autre bloc de mémoire qui a la valeur "dogdog". Parce que dans le backend, une variable ne représente jamais deux blocs de mémoire en même temps. La valeur de (a) avant la concaténation était "dog". Mais après cela (a) représente le "chien de chien", car maintenant (a) dans le backend rep. le bloc qui a la valeur "dogdog". Et "chien" est rep. by (b) et "dog" n'est pas compté comme valeur de garbage jusqu'à ce que (b) représente le "dog".

La confusion est que nous représentons les blocs de mémoire (qui contiennent des données ou des informations) dans le backend avec le même nom de variable.

dineshmehta
la source
-2

Vous pouvez rendre un tableau numpy immuable et utiliser le premier élément:

numpyarrayname[0] = "write once"

puis:

numpyarrayname.setflags(write=False)

ou

numpyarrayname.flags.writeable = False
PauluaP
la source