Sind Python-Strings nicht unveränderlich? Warum funktioniert dann a + “” + b?

109

Mein Verständnis war, dass Python-Strings unveränderlich sind.

Ich habe den folgenden Code ausprobiert:

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

Sollte Python die Zuweisung nicht verhindert haben? Mir fehlt wahrscheinlich etwas.

Irgendeine Idee?

Jason
quelle
55
Die Zeichenfolge selbst ist unveränderlich, aber die Bezeichnung kann sich ändern.
Mitchell
6
Das Zuweisen eines neuen Werts zu einer vorhandenen Variablen ist vollkommen gültig. Python hat keine Konstanten. Dies ist unabhängig von der Mutabilität des Datentyps.
Felix Kling
14
Vielleicht möchten Sie einen Blick auf die id()Funktion werfen . ahat vor und nach der Zuweisung eine andere ID, die angibt, dass sie auf verschiedene Objekte zeigt. Ebenso mit Code wie b = aSie werden feststellen , dass aund bdie gleiche ID haben, an , dass sie das gleiche Objekt sind verweisen.
DRH
Der Link von Delnan ist genau das, worauf ich mich bezog.
Mitchell

Antworten:

179

Zuerst azeigte auf die Zeichenfolge "Hund". Dann haben Sie die Variable so geändert, dass asie auf eine neue Zeichenfolge "Hund isst Leckereien" zeigt. Sie haben die Zeichenfolge "Dog" nicht wirklich mutiert. Zeichenfolgen sind unveränderlich, Variablen können auf alles zeigen, was sie wollen.

Bort
quelle
34
Es ist noch überzeugender, etwas wie x = 'abc' auszuprobieren. x [1] = 'x' in der Python-Repl
xpmatteo
1
Wenn Sie die Interna etwas besser verstehen möchten, lesen Sie meine Antwort. stackoverflow.com/a/40702094/117471
Bruno Bronosky
53

Die String-Objekte selbst sind unveränderlich.

Die Variable, adie auf die Zeichenfolge zeigt, ist veränderbar.

Erwägen:

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
quelle
@ Jason versuchen die gleiche Art von Operationen mit Listen (die veränderlich sind), um den Unterschied zu sehen a.append (3) entspricht a = a + "Foo"
jimifiki
1
@jimifiki a.append(3) ist nicht dasselbe wie a = a + 3. Es ist nicht einmal a += 3(Inplace-Addition ist gleichbedeutend mit .extend, nicht zu .append).
@ Delnan und so, was? Um zu zeigen, dass sich Zeichenfolgen und Listen unterschiedlich verhalten, können Sie davon ausgehen, dass a = a + "Foo" dasselbe ist wie a.append (etwas). Auf jeden Fall ist es nicht dasselbe. Offensichtlich. Waren Sie glücklicher, a.extend ([etwas]) als a.append (etwas) zu lesen? Ich sehe diesen großen Unterschied in diesem Zusammenhang nicht. Aber wahrscheinlich fehlt mir etwas. Die Wahrheit hängt vom Kontext ab
jimifiki
@ Jimifiki: Worüber sprichst du? +verhält sich für Listen und Zeichenfolgen gleich - es wird verkettet, indem eine neue Kopie erstellt wird und keiner der Operanden mutiert wird.
6
Der wirklich wichtige Punkt, der von all dem weggenommen werden muss, ist, dass Zeichenfolgen keine append Funktion haben, weil sie unveränderlich sind.
Lily Chung
46

Die Variable a zeigt auf das Objekt "Hund". Stellen Sie sich die Variable in Python am besten als Tag vor. Sie können den Tag zu verschiedenen Objekten bewegen das ist , was Sie haben , wenn Sie geändert a = "dog"zu a = "dog eats treats".

Unveränderlichkeit bezieht sich jedoch auf das Objekt, nicht auf das Tag.


Wenn Sie versuchen a[1] = 'z', "dog"in zu machen "dzg", würden Sie den Fehler erhalten:

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

Da Zeichenfolgen die Elementzuweisung nicht unterstützen, sind sie unveränderlich.

chrtan
quelle
19

Etwas ist nur dann veränderlich, wenn wir die im Speicherort gespeicherten Werte ändern können, ohne den Speicherort selbst zu ändern.

Der Trick ist: Wenn Sie feststellen, dass der Speicherort vor und nach der Änderung identisch ist, ist er veränderbar.

Zum Beispiel ist die Liste veränderbar. Wie?

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

Eine Zeichenfolge ist unveränderlich. Wie beweisen wir das?

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

wir bekommen

TypeError: Das Objekt 'str' unterstützt keine Elementzuweisung

Wir haben es also nicht geschafft, den String zu mutieren. Dies bedeutet, dass eine Zeichenfolge unveränderlich ist.

Bei der Neuzuweisung ändern Sie die Variable so, dass sie auf einen neuen Speicherort selbst verweist. Hier haben Sie die Zeichenfolge nicht mutiert, sondern die Variable selbst mutiert. Folgendes tun Sie.

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

idvor und nach der Neuzuweisung ist unterschiedlich, was beweist, dass Sie tatsächlich nicht mutieren, sondern die Variable auf einen neuen Speicherort verweisen. Was diesen String nicht mutiert, sondern diese Variable mutiert.

Harish Kayarohanam
quelle
11

Eine Variable ist nur eine Beschriftung, die auf ein Objekt zeigt. Das Objekt ist unveränderlich, aber Sie können die Beschriftung auf ein völlig anderes Objekt verweisen lassen, wenn Sie möchten.

jcollado
quelle
8

Erwägen:

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

Beachten Sie, dass sich der Hex-Speicherort nicht geändert hat, als ich denselben Wert zweimal in der Variablen gespeichert habe. Es hat sich geändert, als ich einen anderen Wert gespeichert habe. Die Zeichenfolge ist unveränderlich. Nicht wegen Eifersucht, sondern weil Sie die Leistungsstrafe zahlen, wenn Sie ein neues Objekt im Speicher erstellen. Die Variable aist nur eine Bezeichnung, die auf diese Speicheradresse verweist. Es kann geändert werden, um auf irgendetwas zu verweisen.

Bruno Bronosky
quelle
7

Die Anweisung a = a + " " + b + " " + ckann anhand von Zeigern aufgeschlüsselt werden.

a + " "sagt, gib mir welche aPunkte, die nicht geändert werden können, und füge " "sie meinem aktuellen Arbeitssatz hinzu.

Erinnerung:

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

+ bsagt gib mir welche bPunkte, die nicht geändert werden können, und füge sie dem aktuellen Arbeitssatz hinzu.

Erinnerung:

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

+ " " + csagt " "zum aktuellen Satz hinzufügen . Geben Sie mir dann die cPunkte an, die nicht geändert werden können, und fügen Sie sie dem aktuellen Arbeitssatz hinzu. Erinnerung:

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

Schließlich a =sagt, setze meinen Zeiger so, dass er auf die resultierende Menge zeigt.

Erinnerung:

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

"Dog"wird zurückgefordert, da keine Zeiger mehr mit dem Speicherblock verbunden sind. Wir haben den Speicherbereich, "Dog"in dem sich befindet, nie geändert , was mit unveränderlich gemeint ist. Wir können jedoch ändern, welche Beschriftungen, falls vorhanden, auf diesen Speicherbereich verweisen.

Spencer Rathbun
quelle
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
quelle
5

Es gibt einen Unterschied zwischen Daten und der Bezeichnung, mit der sie verknüpft sind. Zum Beispiel, wenn Sie es tun

a = "dog"

Die Daten "dog"werden erstellt und unter das Etikett gestellt a. Die Beschriftung kann sich ändern, das, was sich im Speicher befindet, jedoch nicht. Die Daten sind "dog"nach wie vor im Speicher vorhanden (bis der Garbage Collector sie löscht)

a = "cat"

In Ihrem Programm zeigt a^ jetzt auf ^, "cat"aber die Zeichenfolge "dog"hat sich nicht geändert.

Mitchell
quelle
3

Python-Strings sind unveränderlich. Es handelt sich jedoch anicht um eine Zeichenfolge, sondern um eine Variable mit einem Zeichenfolgenwert. Sie können die Zeichenfolge nicht mutieren, aber den Wert der Variablen in eine neue Zeichenfolge ändern.

Michael J. Barber
quelle
2

Variablen können auf eine beliebige Stelle verweisen. Wenn Sie Folgendes tun, wird ein Fehler ausgegeben:

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

Python-String-Objekte sind unveränderlich. Beispiel:

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

In diesem Beispiel können wir sehen, dass wenn wir einen anderen Wert in a zuweisen, dieser nicht geändert wird. Ein neues Objekt wird erstellt.
Und es kann nicht geändert werden. Beispiel:

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

Ein Fehler tritt auf.

Tanim_113
quelle
2

'veränderlich' bedeutet, dass wir den Inhalt der Zeichenfolge ändern können. 'unveränderlich' bedeutet, dass wir keine zusätzliche Zeichenfolge hinzufügen können.

Klicken Sie hier für einen Fotobeweis


quelle
1

>>> a = 'dogs'

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

'dogs eat treats'

>>> print a

'dogs'

Unveränderlich, nicht wahr?!

Der variable Änderungsteil wurde bereits besprochen.

Lohit Bisen
quelle
1
Dies beweist oder widerlegt nicht die Veränderlichkeit von Python-Strings, sondern nur, dass die replace()Methode einen neuen String zurückgibt.
Brent Hronik
1

Betrachten Sie diesen Zusatz zu Ihrem Beispiel

 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

Eine der genaueren Erklärungen, die ich in einem Blog gefunden habe, ist:

In Python ist (fast) alles ein Objekt. Was wir in Python üblicherweise als "Variablen" bezeichnen, werden besser Namen genannt. Ebenso ist "Zuweisung" wirklich die Bindung eines Namens an ein Objekt. Jede Bindung hat einen Bereich, der ihre Sichtbarkeit definiert, normalerweise den Block, aus dem der Name stammt.

Z.B:

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

Wenn wir später some_guy = 'George' sagen, ist das String-Objekt, das 'Fred' enthält, nicht betroffen. Wir haben gerade die Bindung des Namens some_guy geändert. Wir haben jedoch weder die String-Objekte 'Fred' noch 'George' geändert. Für uns können sie auf unbestimmte Zeit weiterleben.

Link zum Blog: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

7to4
quelle
1

Den oben genannten Antworten etwas mehr hinzufügen.

id einer Variablen ändert sich bei Neuzuweisung.

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

Dies bedeutet, dass wir die Variable so mutiert haben, dass sie aauf eine neue Zeichenfolge zeigt. Jetzt existieren zwei string (str) Objekte:

'initial_string'mit id= 139982120425648

und

'new_string'mit id= 139982120425776

Betrachten Sie den folgenden Code:

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

Zeigt nun bauf das 'initial_string'und hat das gleiche idwie avor der Neuzuweisung.

Somit wurde das 'intial_string'nicht mutiert.

Rahul Madiwale
quelle
0

Zusammenfassend:

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

Nicht unveränderlich:

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

Unveränderlich:

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

Dies ist ein Fehler in Python 3, da es unveränderlich ist. Und kein Fehler in Python 2, da es eindeutig nicht unveränderlich ist.

pjf
quelle
0

Die integrierte Funktion id()gibt die Identität eines Objekts als Ganzzahl zurück. Diese Ganzzahl entspricht normalerweise der Position des Objekts im Speicher.

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

139831803293008

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

139831803293120

Zu Beginn wird 'a' im Speicherort 139831803293008 gespeichert, da das Zeichenfolgenobjekt in Python unveränderlich ist, wenn Sie versuchen, die Referenz zu ändern und neu zuzuweisen. Dies wird entfernt und ist ein Zeiger auf einen neuen Speicherort (139831803293120).

learner5060
quelle
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
quelle
2
Während dieser Code das Problem des OP lösen kann, ist es am besten, eine Erklärung beizufügen, wie Ihr Code das Problem des OP behebt. Auf diese Weise können zukünftige Besucher aus Ihrem Beitrag lernen und ihn auf ihren eigenen Code anwenden. SO ist kein Codierungsdienst, sondern eine Ressource für Wissen. Es ist auch wahrscheinlicher, dass qualitativ hochwertige, vollständige Antworten positiv bewertet werden. Diese Funktionen sowie die Anforderung, dass alle Beiträge in sich geschlossen sind, sind einige der Stärken von SO als Plattform, die es von Foren unterscheidet. Sie können bearbeiten, um zusätzliche Informationen hinzuzufügen und / oder Ihre Erklärungen durch Quelldokumentation zu ergänzen
SherylHohman
-1

Dieses Bild gibt die Antwort. Bitte lies es.

Geben Sie hier die Bildbeschreibung ein

Wen Qi
quelle
-1

Wir verketten nur die beiden Zeichenfolgenwerte. Wir ändern niemals den Wert von (a). Gerade jetzt (a) repräsentiert einen anderen Speicherblock, der den Wert "dogdog" hat. Denn im Backend repräsentiert eine Variable niemals zwei Speicherblöcke gleichzeitig. Der Wert von (a) vor der Verkettung war "Hund". Aber danach (a) repräsentieren den "Dogdog", weil jetzt (a) im Backend rep. der Block mit dem Wert "dogdog". Und "Hund" ist rep. durch (b) und "Hund" wird nicht als Müllwert gezählt, bis (b) den "Hund" darstellt.

Die Verwirrung ist, dass wir die Speicherblöcke (die Daten oder Informationen enthalten) im Backend mit demselben Variablennamen darstellen.

Dineshmehta
quelle
-2

Sie können ein Numpy-Array unveränderlich machen und das erste Element verwenden:

numpyarrayname[0] = "write once"

dann:

numpyarrayname.setflags(write=False)

oder

numpyarrayname.flags.writeable = False
PauluaP
quelle