Unveränderliche vs veränderliche Typen


183

Ich bin verwirrt darüber, was ein unveränderlicher Typ ist. Ich weiß, dass das floatObjekt als unveränderlich angesehen wird, mit dieser Art von Beispiel aus meinem Buch:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

Wird dies aufgrund der Klassenstruktur / -hierarchie als unveränderlich angesehen? Die Bedeutung floatsteht an der Spitze der Klasse und ist ein eigener Methodenaufruf. Ähnlich wie bei dieser Art von Beispiel (obwohl mein Buch sagt, dass dictes veränderlich ist):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Während etwas Veränderliches Methoden innerhalb der Klasse hat, mit diesem Beispieltyp:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

Zum letzten Mal class(SortedKeyDict_a), wenn ich diese Art von Set an sie übergebe:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

Ohne die exampleMethode aufzurufen , wird ein Wörterbuch zurückgegeben. Das SortedKeyDictmit __new__kennzeichnet es als Fehler. Ich habe versucht, Ganzzahlen mit an die RoundFloatKlasse zu übergeben, __new__und es wurden keine Fehler angezeigt.


Sie können auch die Listenzuweisung mit [:] und Python überprüfen, wenn copy.copy verwendet werden soll. Auf diese habe ich auch geantwortet, um weitere Informationen zur Veränderlichkeit zu erhalten.
Agf

Antworten:


229

Was? Schwimmer sind unveränderlich? Aber kann ich nicht

x = 5.0
x += 7.0
print x # 12.0

Ist das nicht "mut" x?

Sie stimmen zu, dass Saiten unveränderlich sind, oder? Aber Sie können das Gleiche tun.

s = 'foo'
s += 'bar'
print s # foobar

Der Wert der Variablen ändert sich, aber er ändert sich, indem geändert wird, worauf sich die Variable bezieht. Ein veränderlicher Typ kann sich auf diese Weise ändern, und er kann sich auch "an Ort und Stelle" ändern.

Hier ist der Unterschied.

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

Konkrete Beispiele

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

4
Was Sie erklären, bedeutet für mich: veränderbare Variablen werden als Referenz übergeben, unveränderliche Variablen werden als Wert übergeben. Ist das richtig ?
Lorenz Meyer

16
Fast, aber nicht genau. Technisch gesehen werden alle Variablen in Python als Referenz übergeben, haben jedoch eine Semantik, die eher der Wertübergabe in C entspricht. Ein Gegenbeispiel zu Ihrer Analogie ist, wenn Sie dies tun def f(my_list): my_list = [1, 2, 3]. Bei der Referenzübergabe in C kann sich der Wert des Arguments durch Aufrufen dieser Funktion ändern. In Python macht diese Funktion nichts. def f(my_list): my_list[:] = [1, 2, 3]würde etwas tun.
Morgenstern

6
Veränderbare Typen können an Ort und Stelle geändert werden. Unveränderliche Typen können sich nicht ändern. So sieht Python die Welt. Es ist unabhängig davon, wie Variablen an Funktionen übergeben werden.
Ychaouche

13
Der Hauptunterschied zwischen Pythons Semantik und C ++ - Pass-by-Reference-Semantik besteht darin, dass die Zuweisung in Python keine Mutation ist und in C ++. (Aber das wird natürlich durch die Tatsache kompliziert, dass eine erweiterte Zuordnung, wie a += bmanchmal eine Mutation ist. Und die Tatsache, dass eine Zuordnung zu einem Teil eines größeren Objekts manchmal eine Mutation dieses größeren Objekts bedeutet, nur niemals eine Mutation des Teils - z. B. a[0] = bkeine Mutation a[0], aber es mutiert wahrscheinlich a... Deshalb ist es vielleicht besser, nicht zu versuchen, Dinge in C ++ zu formulieren und stattdessen nur zu beschreiben, was Python in seinen eigenen Begriffen tut ...)
abarnert

2
Ich fand diese Antwort irreführend, weil sie nicht id () verwendet, was wichtig ist, um zu verstehen, was unveränderlich bedeutet.
pawel_winzig

183

Sie müssen verstehen, dass Python alle seine Daten als Objekte darstellt. Einige dieser Objekte wie Listen und Wörterbücher sind veränderbar, dh Sie können ihren Inhalt ändern, ohne ihre Identität zu ändern. Andere Objekte wie Ganzzahlen, Gleitkommazahlen, Zeichenfolgen und Tupel können nicht geändert werden. Ein einfacher Weg, dies zu verstehen, ist, wenn Sie sich eine Objekt-ID ansehen.

Unten sehen Sie eine Zeichenfolge, die unveränderlich ist. Sie können den Inhalt nicht ändern. Es wird ein ausgelöst, TypeErrorwenn Sie versuchen, es zu ändern. Wenn wir neuen Inhalt zuweisen, wird ein neues Objekt erstellt, anstatt den Inhalt zu ändern.

>>> s = "abc"
>>>id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500

Sie können dies mit einer Liste tun, die die Objektidentität nicht ändert

>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

Weitere Informationen zum Python-Datenmodell finden Sie in der Python-Sprachreferenz:


4
+1 Für den Link zu den Python-Dokumenten. Es hat jedoch einige Zeit gedauert, bis mir klar wurde, dass Sie heute zwischen Python 2 und 3 unterscheiden müssen - ich habe die Antwort aktualisiert, um dies hervorzuheben.
Benjamin

106

Gemeinsamer unveränderlicher Typ:

  1. Zahlen: int(),float() ,complex()
  2. unveränderliche Sequenzen: str(), tuple(), frozenset(),bytes()

Allgemeiner veränderlicher Typ (fast alles andere):

  1. veränderbare Sequenzen: list() ,bytearray()
  2. Set-Typ: set()
  3. Zuordnungstyp: dict()
  4. Klassen, Klasseninstanzen
  5. etc.

Ein Trick, um schnell zu testen, ob ein Typ veränderlich ist oder nicht, ist die Verwendung id() integrierten Funktion.

Beispiele für die Verwendung einer Ganzzahl,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

mit auf Liste,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)

11
Gut erklärt. Gefiel das Konzept der Überprüfung durch id(). +1.
Parag Tyagi

4
Eigentlich ist die Verwendung von id()hier irreführend. Ein bestimmtes Objekt hat während seiner Lebensdauer immer dieselbe ID, aber unterschiedliche Objekte, die zu unterschiedlichen Zeiten vorhanden sind, können aufgrund der Speicherbereinigung dieselbe ID haben.
Augurar

36

Erstens hat es nichts mit Veränderlichkeit zu tun, ob eine Klasse Methoden hat oder welche Klassenstruktur sie hat.

ints und floats sind unveränderlich . Wenn ich mache

a = 1
a += 5

Es zeigt den Namen aauf eine Stelle 1im Speicher in der ersten Zeile. In der zweiten Zeile wird nachgeschlagen, dass 1hinzugefügt 5, erhalten 6und dann Punkte angezeigt werdena an , dass 6im Speicher - es nicht ändern , die 1zu einem 6in keiner Weise. Die gleiche Logik gilt für die folgenden Beispiele unter Verwendung anderer unveränderlicher Typen:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

Bei veränderlichen Typen kann ich Dinge tun, die den Wert, in dem er gespeichert ist, tatsächlich ändern . Mit:

d = [1, 2, 3]

Ich habe eine Liste der Standorte von erstellt 1, 2und 3im Speicher. Wenn ich es dann tue

e = d

Ich zeige nur e auf die gleichenlist d Punkte. Ich kann dann tun:

e += [4, 5]

Und die Liste, auf die sowohl eals auch dzeigt, wird aktualisiert, um auch die Speicherorte von 4und 5im Speicher zu haben.

Wenn ich zurück zu einem gehe unveränderlichen Typ zurückkehre und dies mit einem tuple:

f = (1, 2, 3)
g = f
g += (4, 5)

Dann zeigt fimmer noch nur auf das Originaltuple - Sie haben gauf ein völlig neuestuple hingewiesen .

Nun mit Ihrem Beispiel von

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Wo du vorbeigehst

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(das ist ein tuplevon tuples) as val, Sie erhalten einen Fehler, weil tuples keine .clear()Methode haben - Sie müssten übergeben dict(d), valdamit es funktioniert. In diesem Fall erhalten Sie eine leere SortedKeyDict.


2
Das ist eine sehr gute Erklärung. Liebte diese Frage und viele interessante (neue) Perspektiven, um sie zu erklären.
Gescheiterter Wissenschaftler

24

Wenn Sie aus einer anderen Sprache zu Python kommen (außer einer, die Python sehr ähnlich ist, wie Ruby) und darauf bestehen, es in Bezug auf diese andere Sprache zu verstehen, sind die Leute hier normalerweise verwirrt:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

In Python ist die Zuweisung in Python keine Mutation.

Wenn Sie in C ++ schreiben a = 2, rufen Sie auf a.operator=(2), wodurch das in gespeicherte Objekt mutiert wird a. (Und wenn es war kein Objekt gespeichert a, dass ein Fehler ist.)

Tut in Python a = 2nichts mit dem, was in gespeichert wurde a; es bedeutet nur, dass 2jetzt in gespeichert awird. (Und wenn es ist kein Objekt gespeichert ina , ist das in Ordnung.)


Letztendlich ist dies Teil einer noch tieferen Unterscheidung.

Eine Variable in einer Sprache wie C ++ ist ein typisierter Speicherort im Speicher. Wenn dies aein intist, bedeutet dies, dass es 4 Bytes sind, von denen der Compiler weiß, dass sie als interpretiert werden sollen int. Also, wenn Sie das tun a = 2, ändert es , was in diesen 4 Byte Speicher gespeichert wird aus 0, 0, 0, 1zu 0, 0, 0, 2. Wenn es irgendwo anders eine andere int-Variable gibt, hat sie ihre eigenen 4 Bytes.

Eine Variable in einer Sprache wie Python ist ein Name für ein Objekt, das ein Eigenleben hat. Es gibt ein Objekt für die Nummer 1und ein anderes Objekt für die Nummer 2. Und asind nicht 4 Bytes Speicher, die als dargestellt werden int, sondern nur ein Name, der auf das 1Objekt zeigt. Es macht keinen Sinn a = 2, die Nummer 1 in die Nummer 2 zu verwandeln (das würde jedem Python-Programmierer zu viel Macht geben, um die grundlegenden Funktionen des Universums zu ändern). Stattdessen avergessen Sie einfach das 1Objekt und zeigen auf das2 stattdessen Objekt.


Also, wenn Zuweisung keine Mutation ist, was ist es dann ? eine Mutation?

  • Aufrufen einer Methode, deren Mutation dokumentiert ist, wie z a.append(b) . (Beachten Sie, dass diese Methoden fast immer zurückkehren None). Unveränderliche Typen haben keine solchen Methoden, veränderbare Typen normalerweise.
  • Zuweisen zu einem Teil des Objekts, wie a.spam = bodera[0] = b . Unveränderliche Typen erlauben keine Zuordnung zu Attributen oder Elementen, veränderbare Typen erlauben normalerweise das eine oder andere.
  • Manchmal mit erweiterter Zuordnung a += b, manchmal nicht. Veränderbare Typen mutieren normalerweise den Wert; Unveränderliche Typen tun dies niemals und geben Ihnen stattdessen eine Kopie (sie berechnen a + bund weisen das Ergebnis dann zu a).

Aber wenn Zuweisung keine Mutation ist, wie ist die Zuweisung zu einem Teil der Objektmutation? Dort wird es schwierig. a[0] = bist nicht mutieren a[0](auch hier im Gegensatz zu C ++), aber es tut mutierena ( im Gegensatz zu C ++, mit Ausnahme indirekt).

All dies ist der Grund, warum es wahrscheinlich besser ist, nicht zu versuchen, Pythons Semantik in eine Sprache zu bringen, die Sie gewohnt sind, sondern stattdessen Pythons Semantik nach ihren eigenen Begriffen zu lernen.


2
Sagen Sie a = 'hi'. a [0] = 'f' hat 'print a' print 'fi' (Habe ich bisher recht?). Wenn Sie also sagen, dass es nicht eine [0], sondern eine a mutiert, was bedeutet das? ? Hat ein [n] jetzt auch einen eigenen Platz und wird es durch Ändern seines Werts auf einen anderen Wert gesetzt?
Daniel Springer

19

Ob ein Objekt veränderlich ist oder nicht, hängt von seinem Typ ab. Dies hängt weder davon ab, ob bestimmte Methoden vorhanden sind, noch von der Struktur der Klassenhierarchie.

Benutzerdefinierte Typen (dh Klassen) sind im Allgemeinen veränderbar. Es gibt einige Ausnahmen, z. B. einfache Unterklassen eines unveränderlichen Typs. Andere unveränderliche Typen gehören einige integrierte Typen wie int, float, tupleundstr sowie einige Python - Klassen in C implementiert

Eine allgemeine Erklärung aus dem Kapitel "Datenmodell" in der Python-Sprachreferenz " :

Der Wert einiger Objekte kann sich ändern. Objekte, deren Wert sich ändern kann, gelten als veränderlich. Objekte, deren Wert nach ihrer Erstellung unveränderlich ist, werden als unveränderlich bezeichnet.

(Der Wert eines unveränderlichen Containerobjekts, das einen Verweis auf ein veränderliches Objekt enthält, kann sich ändern, wenn dessen Wert geändert wird. Der Container wird jedoch weiterhin als unveränderlich betrachtet, da die darin enthaltene Sammlung von Objekten nicht geändert werden kann. Daher ist die Unveränderlichkeit nicht streng das gleiche wie ein unveränderlicher Wert, ist es subtiler.)

Die Veränderbarkeit eines Objekts wird durch seinen Typ bestimmt. Beispielsweise sind Zahlen, Zeichenfolgen und Tupel unveränderlich, während Wörterbücher und Listen veränderbar sind.


+1 Beachten Sie jedoch, dass nur einige Erweiterungstypen (möglicherweise möchten Sie Ihre Definition davon überprüfen, alle in Python integrierten Typen sind in C implementiert) unveränderlich sind. Andere (die meisten, würde ich sagen) sind vollkommen veränderlich.

@delnan Wie nennt man "Erweiterungstypen" ?
Eyquem

@eyquem: Ich habe den Begriff "Erweiterungstypen" in meiner Antwort falsch verwendet, und delnan hat sich darauf bezogen. Nach seinem Kommentar überarbeitete ich meine Antwort und vermied es, diesen Begriff zu verwenden.
Taleinat

19

Unterschied zwischen veränderlichem und unveränderlichem Objekt

Definitionen

Veränderbares Objekt : Objekt, das nach dem Erstellen geändert werden kann.
Unveränderliches Objekt : Objekt, das nach dem Erstellen nicht geändert werden kann.

In Python wird versucht, den Wert des unveränderlichen Objekts zu ändern, das es dem neuen Objekt gibt.

Veränderbare Objekte

Hier sind die Listenobjekte in Python, die vom veränderlichen Typ sind:

  1. list
  2. Dictionary
  3. Set
  4. bytearray
  5. user defined classes

Unveränderliche Objekte

Hier sind die Listenobjekte in Python, die vom unveränderlichen Typ sind:

  1. int
  2. float
  3. decimal
  4. complex
  5. bool
  6. string
  7. tuple
  8. range
  9. frozenset
  10. bytes

Einige unbeantwortete Fragen

Fragen : Ist String ein unveränderlicher Typ?
Antwort : Ja , aber können Sie das erklären: Beweis 1 :

a = "Hello"
a +=" World"
print a

Ausgabe

"Hello World"

Im obigen Beispiel wurde die Zeichenfolge einmal erstellt, als "Hallo" schließlich in "Hallo Welt" geändert wurde. Dies impliziert, dass der String vom veränderlichen Typ ist. Aber es ist nicht so, dass wir seine Identität überprüfen und prüfen können, ob es sich um einen veränderlichen Typ handelt oder nicht.

a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
    print "String is Immutable"

Ausgabe

String is Immutable

Beweis 2 :

a = "Hello World"
a[0] = "M"

Ausgabe

TypeError 'str' object does not support item assignment

Fragen : Ist Tupel ein unveränderlicher Typ?
Antwort : Ja, es ist Beweis 1 :

tuple_a = (1,)
tuple_a[0] = (2,)
print a

Ausgabe

'tuple' object does not support item assignment

In [46]: a = "Hallo" In [47]: id (a) Out [47]: 140071263880128 In [48]: a = a.replace ("H", "g") In [49]: a Out [49]: 'gello' In [50]: id (a) Out [50]: 140071263881040
Argus Malware

Möchten Sie Ihr Problem mit der
Artikelzuweisung

Die Artikelzuordnung ist bei unveränderlichen Typen nicht problematisch. In Ihrem Fall ändern Sie die Zeichenfolge a, aber im Speicher wird sie einer neuen Variablen zugewiesen. Die Zuweisung von Elementen in meinem Fall ändert den Speicher der Variablen nicht wie im Fall einer Liste oder eines Wörterbuchs. Wenn Sie ersetzen, erstellen Sie eine neue Variable, ohne die vorhandene Variable zu
ändern

@ArgusMalware In Ihrem Fall sind zwei IDs gleich, da die erste von GC recycelt wird, sodass die zweite den Speicher wiederverwendet.
Cologler

11

Ein veränderbares Objekt muss mindestens eine Methode haben, mit der das Objekt mutiert werden kann. Das listObjekt verfügt beispielsweise über die appendMethode, mit der das Objekt tatsächlich mutiert wird:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

Die Klasse floatverfügt jedoch über keine Methode zum Mutieren eines Float-Objekts. Du kannst tun:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

Der =Operand ist jedoch keine Methode. Es wird nur eine Bindung zwischen der Variablen und dem, was rechts davon ist, hergestellt, sonst nichts. Es ändert oder erstellt niemals Objekte. Es ist eine Deklaration dessen, worauf die Variable seitdem verweist.

Wenn Sie dies tun, bindet b = b + 0.1der =Operand die Variable an einen neuen Float, der mit dem Ergebnis von erstellt wird 5 + 0.1.

Wenn Sie einem vorhandenen Objekt eine Variable zuweisen =, ob veränderlich oder nicht, bindet der Operand die Variable an dieses Objekt. Und nichts passiert mehr

In beiden Fällen =machen die nur die Bindung. Es werden keine Objekte geändert oder erstellt.

Wenn Sie dies tun a = 1.0, ist der =Operand nicht der Float, sondern der 1.0Teil der Zeile. Wenn Sie schreiben 1.0, ist dies eine Abkürzung für float(1.0)einen Konstruktoraufruf, der ein float-Objekt zurückgibt. (Dies ist der Grund, warum Sie beim 1.0Eingeben und Drücken der Eingabetaste das "Echo" erhalten.1.0 das unten stehende angezeigt. Dies ist der Rückgabewert der von Ihnen aufgerufenen Konstruktorfunktion.)

Wenn bes sich nun um ein Float handelt und Sie es zuweisen a = b, zeigen beide Variablen auf dasselbe Objekt, aber tatsächlich können die Variablen nicht miteinander kommunizieren, da das Objekt unveränderlich ist. Wenn Sie dies tun b += 1, bzeigen Sie jetzt auf ein neues Objekt und sind aes zeigt immer noch auf den Oldone und kann nicht wissen, worauf er bzeigt.

aber wenn cist, sagen wir, a list, und Sie weisen a = cjetzt zu aund ckönnen "kommunizieren", weil listveränderlich ist, und wenn Sie dies tun c.append('msg'), dann überprüfen Sie einfacha Sie Sie die Nachricht erhalten.

(Übrigens hat jedes Objekt eine eindeutige ID-Nummer, mit der Sie eine eindeutige ID erhalten können id(x). Sie können also überprüfen, ob ein Objekt identisch ist oder nicht, ob sich seine eindeutige ID geändert hat.)


6

Eine Klasse ist unveränderlich, wenn jedes Objekt dieser Klasse bei der Instanziierung einen festen Wert hat, der nicht nachträglich geändert werden kann

Mit anderen Worten, ändern Sie den gesamten Wert dieser Variablen (name)oder lassen Sie ihn in Ruhe.

Beispiel:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

Sie haben erwartet, dass dies funktioniert und Hallo Welt druckt, aber dies wird den folgenden Fehler auslösen:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

Der Interpreter sagt: Ich kann das erste Zeichen dieser Zeichenfolge nicht ändern

Sie müssen das Ganze ändern string, damit es funktioniert:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

Überprüfen Sie diese Tabelle:

Geben Sie hier die Bildbeschreibung ein

Quelle


Wie kann man Komponenten einer Python-Zeichenfolge präziser ändern, als Sie oben gezeigt haben?
Luke Davis

@ LukeDavis Du könntest tun my_string = 'h' + my_string[1:]. Dadurch wird eine neue Zeichenfolge mit dem Namen my_string generiert, und die ursprüngliche Zeichenfolge my_string ist nicht mehr vorhanden (drucken id(my_string), um dies anzuzeigen ). Das ist natürlich nicht sehr flexibel, für den allgemeineren Fall könnten Sie in Liste und zurück konvertieren:l = list(my_string) l[0] = 'h' my_string = ''.join(l)
Danio

5

Es scheint mir, dass Sie mit der Frage kämpfen, was veränderlich / unveränderlich eigentlich bedeutet . Hier ist eine einfache Erklärung:

Zuerst brauchen wir eine Grundlage, auf der die Explenation basiert.

Stellen Sie sich also alles vor, was Sie als virtuelles Objekt programmieren, etwas, das als Folge von Binärzahlen im Arbeitsspeicher eines Computers gespeichert ist. (Versuchen Sie sich das jedoch nicht zu sehr vorzustellen. ^^) In den meisten Computersprachen arbeiten Sie jetzt nicht direkt mit diesen Binärzahlen, sondern verwenden eher eine Interpretation von Binärzahlen.

ZB denken Sie nicht an Zahlen wie 0x110, 0xaf0278297319 oder ähnliches, sondern an Zahlen wie 6 oder Strings wie "Hallo Welt". Trotzdem sind diese Zahlen oder Strings eine Interpretation einer Binärzahl im Computerspeicher. Gleiches gilt für jeden Wert einer Variablen.

Kurzum: Wir programmieren nicht mit tatsächlichen Werten, sondern mit Interpretationen tatsächlicher Binärwerte.

Jetzt haben wir Interpretationen, die aus Gründen der Logik und anderer "netter Dinge" nicht geändert werden dürfen, während es Interpretationen gibt, die durchaus geändert werden können. Stellen Sie sich zum Beispiel die Simulation einer Stadt vor, also ein Programm, in dem es viele virtuelle Objekte gibt und einige davon Häuser sind. Können diese virtuellen Objekte (die Häuser) nun geändert werden und können sie immer noch als dieselben Häuser betrachtet werden? Natürlich können sie das. Sie sind also veränderlich: Sie können verändert werden, ohne ein "völlig" anderes Objekt zu werden.

Denken Sie jetzt an ganze Zahlen: Dies sind auch virtuelle Objekte (Folgen von Binärzahlen in einem Computerspeicher). Wenn wir also einen von ihnen ändern, z. B. den Wert sechs um eins erhöhen, ist es dann immer noch eine Sechs? Na klar nicht. Somit ist jede ganze Zahl unveränderlich.

Also: Wenn eine Änderung an einem virtuellen Objekt bedeutet, dass es tatsächlich zu einem anderen virtuellen Objekt wird, wird es als unveränderlich bezeichnet.

Schlussbemerkungen:

(1) Verwechseln Sie niemals Ihre reale Erfahrung von veränderlich und unveränderlich mit der Programmierung in einer bestimmten Sprache:

Jede Programmiersprache hat eine eigene Definition, welche Objekte stummgeschaltet werden dürfen und welche nicht.

Während Sie jetzt vielleicht den Unterschied in der Bedeutung verstehen, müssen Sie dennoch die tatsächliche Implementierung für jede Programmiersprache lernen. ... In der Tat könnte es einen Zweck einer Sprache geben, in der eine 6 stummgeschaltet werden kann, um eine 7 zu werden. Andererseits wäre dies ein ziemlich verrücktes oder interessantes Zeug, wie Simulationen paralleler Universen. ^^

(2) Diese Erklärung ist sicherlich nicht wissenschaftlich, sondern soll Ihnen helfen, den Unterschied zwischen veränderlich und unveränderlich zu erfassen.


5

Das Ziel dieser Antwort ist es, einen einzigen Ort zu schaffen, an dem Sie alle guten Ideen finden, wie Sie feststellen können, ob es sich um Mutation / Nonmutating (unveränderlich / veränderlich) handelt, und wo möglich, was Sie dagegen tun können. Es gibt Zeiten, in denen eine Mutation unerwünscht ist und sich das diesbezügliche Verhalten von Python für Codierer, die aus anderen Sprachen kommen, als nicht intuitiv anfühlt.

Laut einem nützlichen Beitrag von @ mina-gabriel:

Analysieren Sie das Obige und kombinieren Sie es mit einem Beitrag von @ arrakëën:

Was kann sich nicht unerwartet ändern?

  • Skalare (Variablentypen, die einen einzelnen Wert speichern) ändern sich nicht unerwartet
    • numerische Beispiele: int (), float (), complex ()
  • Es gibt einige "veränderbare Sequenzen":
    • str (), tuple (), frozenset (), bytes ()

Was kann?

  • Liste wie Objekte (Listen, Wörterbücher, Mengen, bytearray ())
  • Ein Beitrag hier sagt auch Klassen und Klasseninstanzen, aber dies kann davon abhängen, wovon die Klasse erbt und / oder wie sie aufgebaut ist.

Mit "unerwartet" meine ich, dass Programmierer aus anderen Sprachen dieses Verhalten möglicherweise nicht erwarten (mit Ausnahme von Ruby und möglicherweise einigen anderen "Python-ähnlichen" Sprachen).

Zu dieser Diskussion hinzufügen:

Dieses Verhalten ist von Vorteil, wenn Sie verhindern können, dass Sie Ihren Code versehentlich mit mehreren Kopien speicherfressender großer Datenstrukturen füllen. Aber wenn dies unerwünscht ist, wie können wir es umgehen?

Bei Listen besteht die einfache Lösung darin, eine neue wie folgt zu erstellen:

list2 = list (list1)

mit anderen Strukturen ... kann die Lösung schwieriger sein. Eine Möglichkeit besteht darin, die Elemente zu durchlaufen und sie einer neuen leeren Datenstruktur (desselben Typs) hinzuzufügen.

Funktionen können das Original mutieren, wenn Sie veränderbare Strukturen übergeben. Wie kann man das sagen?

  • Es gibt einige Tests zu anderen Kommentaren in diesem Thread, aber dann gibt es Kommentare, die darauf hinweisen, dass diese Tests kein vollständiger Beweis sind
  • object.function () ist eine Methode des ursprünglichen Objekts, aber nur einige davon mutieren. Wenn sie nichts zurückgeben, tun sie es wahrscheinlich. Man würde erwarten, dass .append () mutiert, ohne es unter seinem Namen zu testen. .union () gibt die Vereinigung von set1.union (set2) zurück und mutiert nicht. Im Zweifelsfall kann die Funktion auf einen Rückgabewert überprüft werden. Wenn return = None ist, mutiert es nicht.
  • sorted () kann in einigen Fällen eine Problemumgehung sein. Da eine sortierte Version des Originals zurückgegeben wird, können Sie eine nicht mutierte Kopie speichern, bevor Sie auf andere Weise mit der Arbeit am Original beginnen. Bei dieser Option wird jedoch davon ausgegangen, dass Sie sich nicht um die Reihenfolge der ursprünglichen Elemente kümmern (wenn Sie dies tun, müssen Sie einen anderen Weg finden). Im Gegensatz dazu mutiert .sort () das Original (wie zu erwarten).

Nicht standardmäßige Ansätze (falls hilfreich): Gefunden auf Github unter einer MIT-Lizenz veröffentlicht:

  • Github-Repository unter: tobgu benannt: pyrsistent
  • Was es ist: Persistenter Python-Datenstrukturcode, der geschrieben wurde, um anstelle von Kerndatenstrukturen verwendet zu werden, wenn eine Mutation unerwünscht ist

Für benutzerdefinierte Klassen schlägt @semicolon vor, zu überprüfen, ob eine __hash__Funktion vorhanden ist , da veränderbare Objekte im Allgemeinen keine __hash__()Funktion haben sollten.

Das ist alles, was ich zu diesem Thema angehäuft habe. Andere Ideen, Korrekturen usw. sind willkommen. Vielen Dank.


3

Eine Art, über den Unterschied nachzudenken:

Zuweisungen zu unveränderlichen Objekten in Python können als tiefe Kopien betrachtet werden, während Zuweisungen zu veränderlichen Objekten flach sind


1
Das ist falsch. Alle Zuweisungen in Python beziehen sich auf Referenz. Es ist kein Kopieren erforderlich.
Augurar

3

Die einfachste Antwort:

Eine veränderbare Variable ist eine Variable, deren Wert sich an Ort und Stelle ändern kann, während bei einer unveränderlichen Variablen eine Wertänderung nicht an Ort und Stelle erfolgt. Durch Ändern einer unveränderlichen Variablen wird dieselbe Variable neu erstellt.

Beispiel:

>>>x = 5

Erstellt einen Wert 5, auf den x verweist

x -> 5

>>>y = x

Diese Aussage lässt y auf 5 von x verweisen

x -------------> 5 <----------- y

>>>x = x + y

Als x wurde eine Ganzzahl (unveränderlicher Typ) neu erstellt.

In der Anweisung ergibt der Ausdruck auf RHS den Wert 10, und wenn dieser LHS (x) zugewiesen wird, wird x auf 10 neu erstellt

x ---------> 10

y ---------> 5


-1

Ich habe nicht alle Antworten gelesen, aber die ausgewählte Antwort ist nicht korrekt und ich denke, der Autor hat die Idee, dass die Möglichkeit, eine Variable neu zuzuweisen, bedeutet, dass jeder Datentyp veränderbar ist. Das ist nicht der Fall. Veränderlichkeit hat eher mit Referenzübergabe als mit Wertübergabe zu tun.

Nehmen wir an, Sie haben eine Liste erstellt

a = [1,2]

Wenn Sie sagen würden:

b = a
b[1] = 3

Obwohl Sie einen Wert für B neu zugewiesen haben, wird auch der Wert für a neu zugewiesen. Es liegt daran, wenn Sie "b = a" zuweisen. Sie übergeben die "Referenz" an das Objekt und nicht an eine Kopie des Werts. Dies ist bei Strings, Floats usw. nicht der Fall. Dies macht Listen, Wörterbücher und dergleichen veränderlich, aber Boolesche Werte, Floats usw. unveränderlich.


-1

Bei unveränderlichen Objekten wird durch die Zuweisung beispielsweise eine neue Kopie der Werte erstellt.

x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot 
#effect the value of y
print(x,y)

Bei veränderlichen Objekten erstellt die Zuweisung keine weitere Kopie der Werte. Beispielsweise,

x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy 
x[2]=5
print(x,y) # both x&y holds the same list

1
Absolut falsch. Die Zuordnung erstellt niemals eine Kopie . Bitte lesen Sie nedbatchelder.com/text/names.html. Im ersten Fall x=10handelt es sich lediglich um eine andere Zuweisung , während x[2] = 5eine Mutator-Methode aufgerufen wird. intObjekten fehlen einfach Mutator-Methoden , aber die Semantik der Python-Zuweisung hängt nicht vom Typ ab
juanpa.arrivillaga

-2

In Python gibt es einen einfachen Weg zu wissen:

Unveränderlich:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

Veränderlich:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

Und:

>>> s=abs
>>> s is abs
True

Daher denke ich, dass die integrierte Funktion auch in Python unveränderlich ist.

Aber ich verstehe wirklich nicht, wie Float funktioniert:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

Es ist so seltsam.


Das ist aber eindeutig nicht gültig. Weil Tupel unveränderlich sind. Tippe x = (1, 2)und versuche dann zu mutieren x, es ist nicht möglich. Eine Möglichkeit, die ich auf Mutabilität überprüft habe, besteht darin hash, dass sie zumindest für die eingebauten Objekte funktioniert. hash(1) hash('a') hash((1, 2)) hash(True)Alle arbeiten und hash([]) hash({}) hash({1, 2})alle funktionieren nicht.
Semikolon

@semicolon Für benutzerdefinierte Klassen hash()funktioniert dies dann, wenn das Objekt eine __hash__()Methode definiert , obwohl benutzerdefinierte Klassen im Allgemeinen veränderbar sind.
Augurar

1
@augurar Ich meine ja, aber nichts in Python garantiert etwas, weil Python keine echte statische Typisierung oder formale Garantien hat. Aber die hashMethode ist immer noch ziemlich gut, da veränderbare Objekte im Allgemeinen keine __hash__()Methode haben sollten, da es nur gefährlich ist, sie zu Schlüsseln in einem Wörterbuch zu machen.
Semikolon

1
@augurar und Semikolon (oder andere, wenn sie es wissen): __hash __ () Lösung ... muss der Ersteller einer benutzerdefinierten Klasse sie hinzufügen, damit sie vorhanden ist? Wenn ja, dann ist die Regel, wenn vorhanden, sollte das Objekt unveränderlich sein; Wenn es nicht existiert, können wir es nicht sagen, da der Schöpfer möglicherweise einfach aufgehört hat, wenn er ausgeschaltet ist.
TMWP
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.