Ich bin verwirrt darüber, was ein unveränderlicher Typ ist. Ich weiß, dass das float
Objekt 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 float
steht an der Spitze der Klasse und ist ein eigener Methodenaufruf. Ähnlich wie bei dieser Art von Beispiel (obwohl mein Buch sagt, dass dict
es 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 example
Methode aufzurufen , wird ein Wörterbuch zurückgegeben. Das SortedKeyDict
mit __new__
kennzeichnet es als Fehler. Ich habe versucht, Ganzzahlen mit an die RoundFloat
Klasse zu übergeben, __new__
und es wurden keine Fehler angezeigt.
quelle
Antworten:
Was? Schwimmer sind unveränderlich? Aber kann ich nicht
Ist das nicht "mut" x?
Sie stimmen zu, dass Saiten unveränderlich sind, oder? Aber Sie können das Gleiche tun.
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.
Konkrete Beispiele
quelle
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.a += b
manchmal 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] = b
keine Mutationa[0]
, aber es mutiert wahrscheinlicha
... 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 ...)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,
TypeError
wenn Sie versuchen, es zu ändern. Wenn wir neuen Inhalt zuweisen, wird ein neues Objekt erstellt, anstatt den Inhalt zu ändern.Sie können dies mit einer Liste tun, die die Objektidentität nicht ändert
Weitere Informationen zum Python-Datenmodell finden Sie in der Python-Sprachreferenz:
quelle
Gemeinsamer unveränderlicher Typ:
int()
,float()
,complex()
str()
,tuple()
,frozenset()
,bytes()
Allgemeiner veränderlicher Typ (fast alles andere):
list()
,bytearray()
set()
dict()
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,
mit auf Liste,
quelle
id()
. +1.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.Erstens hat es nichts mit Veränderlichkeit zu tun, ob eine Klasse Methoden hat oder welche Klassenstruktur sie hat.
int
s undfloat
s sind unveränderlich . Wenn ich macheEs zeigt den Namen
a
auf eine Stelle1
im Speicher in der ersten Zeile. In der zweiten Zeile wird nachgeschlagen, dass1
hinzugefügt5
, erhalten6
und dann Punkte angezeigt werdena
an , dass6
im Speicher - es nicht ändern , die1
zu einem6
in keiner Weise. Die gleiche Logik gilt für die folgenden Beispiele unter Verwendung anderer unveränderlicher Typen:Bei veränderlichen Typen kann ich Dinge tun, die den Wert, in dem er gespeichert ist, tatsächlich ändern . Mit:
Ich habe eine Liste der Standorte von erstellt
1
,2
und3
im Speicher. Wenn ich es dann tueIch zeige nur
e
auf die gleichenlist
d
Punkte. Ich kann dann tun:Und die Liste, auf die sowohl
e
als auchd
zeigt, wird aktualisiert, um auch die Speicherorte von4
und5
im Speicher zu haben.Wenn ich zurück zu einem gehe unveränderlichen Typ zurückkehre und dies mit einem
tuple
:Dann zeigt
f
immer noch nur auf das Originaltuple
- Sie habeng
auf ein völlig neuestuple
hingewiesen .Nun mit Ihrem Beispiel von
Wo du vorbeigehst
(das ist ein
tuple
vontuples
) asval
, Sie erhalten einen Fehler, weiltuple
s keine.clear()
Methode haben - Sie müssten übergebendict(d)
,val
damit es funktioniert. In diesem Fall erhalten Sie eine leereSortedKeyDict
.quelle
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:
In Python ist die Zuweisung in Python keine Mutation.
Wenn Sie in C ++ schreiben
a = 2
, rufen Sie aufa.operator=(2)
, wodurch das in gespeicherte Objekt mutiert wirda
. (Und wenn es war kein Objekt gespeicherta
, dass ein Fehler ist.)Tut in Python
a = 2
nichts mit dem, was in gespeichert wurdea
; es bedeutet nur, dass2
jetzt in gespeicherta
wird. (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
a
einint
ist, bedeutet dies, dass es 4 Bytes sind, von denen der Compiler weiß, dass sie als interpretiert werden sollenint
. Also, wenn Sie das tuna = 2
, ändert es , was in diesen 4 Byte Speicher gespeichert wird aus0, 0, 0, 1
zu0, 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
1
und ein anderes Objekt für die Nummer2
. Unda
sind nicht 4 Bytes Speicher, die als dargestellt werdenint
, sondern nur ein Name, der auf das1
Objekt zeigt. Es macht keinen Sinna = 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). Stattdessena
vergessen Sie einfach das1
Objekt und zeigen auf das2
stattdessen Objekt.Also, wenn Zuweisung keine Mutation ist, was ist es dann ? eine Mutation?
a.append(b)
. (Beachten Sie, dass diese Methoden fast immer zurückkehrenNone
). Unveränderliche Typen haben keine solchen Methoden, veränderbare Typen normalerweise.a.spam = b
odera[0] = b
. Unveränderliche Typen erlauben keine Zuordnung zu Attributen oder Elementen, veränderbare Typen erlauben normalerweise das eine oder andere.a += b
, manchmal nicht. Veränderbare Typen mutieren normalerweise den Wert; Unveränderliche Typen tun dies niemals und geben Ihnen stattdessen eine Kopie (sie berechnena + b
und weisen das Ergebnis dann zua
).Aber wenn Zuweisung keine Mutation ist, wie ist die Zuweisung zu einem Teil der Objektmutation? Dort wird es schwierig.
a[0] = b
ist nicht mutierena[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.
quelle
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
,tuple
undstr
sowie einige Python - Klassen in C implementiertEine allgemeine Erklärung aus dem Kapitel "Datenmodell" in der Python-Sprachreferenz " :
quelle
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:
list
Dictionary
Set
bytearray
user defined classes
Unveränderliche Objekte
Hier sind die Listenobjekte in Python, die vom unveränderlichen Typ sind:
int
float
decimal
complex
bool
string
tuple
range
frozenset
bytes
Einige unbeantwortete Fragen
Fragen : Ist String ein unveränderlicher Typ?
Antwort : Ja , aber können Sie das erklären: Beweis 1 :
Ausgabe
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.
Ausgabe
Beweis 2 :
Ausgabe
Fragen : Ist Tupel ein unveränderlicher Typ?
Antwort : Ja, es ist Beweis 1 :
Ausgabe
quelle
Ein veränderbares Objekt muss mindestens eine Methode haben, mit der das Objekt mutiert werden kann. Das
list
Objekt verfügt beispielsweise über dieappend
Methode, mit der das Objekt tatsächlich mutiert wird:Die Klasse
float
verfügt jedoch über keine Methode zum Mutieren eines Float-Objekts. Du kannst tun: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.1
der=
Operand die Variable an einen neuen Float, der mit dem Ergebnis von erstellt wird5 + 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 mehrIn 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 der1.0
Teil der Zeile. Wenn Sie schreiben1.0
, ist dies eine Abkürzung fürfloat(1.0)
einen Konstruktoraufruf, der ein float-Objekt zurückgibt. (Dies ist der Grund, warum Sie beim1.0
Eingeben 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
b
es sich nun um ein Float handelt und Sie es zuweisena = 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 tunb += 1
,b
zeigen Sie jetzt auf ein neues Objekt und sinda
es zeigt immer noch auf den Oldone und kann nicht wissen, worauf erb
zeigt.aber wenn
c
ist, sagen wir, alist
, und Sie weisena = c
jetzt zua
undc
können "kommunizieren", weillist
veränderlich ist, und wenn Sie dies tunc.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.)quelle
Mit anderen Worten, ändern Sie den gesamten Wert dieser Variablen
(name)
oder lassen Sie ihn in Ruhe.Beispiel:
Sie haben erwartet, dass dies funktioniert und Hallo Welt druckt, aber dies wird den folgenden Fehler auslösen:
Der Interpreter sagt: Ich kann das erste Zeichen dieser Zeichenfolge nicht ändern
Sie müssen das Ganze ändern
string
, damit es funktioniert:Überprüfen Sie diese Tabelle:
Quelle
quelle
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 (druckenid(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)
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.
quelle
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?
Was kann?
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?
Nicht standardmäßige Ansätze (falls hilfreich): Gefunden auf Github unter einer MIT-Lizenz veröffentlicht:
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.
quelle
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
quelle
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:
Erstellt einen Wert 5, auf den x verweist
x -> 5
Diese Aussage lässt y auf 5 von x verweisen
x -------------> 5 <----------- 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
quelle
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
Wenn Sie sagen würden:
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.
quelle
Bei unveränderlichen Objekten wird durch die Zuweisung beispielsweise eine neue Kopie der Werte erstellt.
Bei veränderlichen Objekten erstellt die Zuweisung keine weitere Kopie der Werte. Beispielsweise,
quelle
x=10
handelt es sich lediglich um eine andere Zuweisung , währendx[2] = 5
eine Mutator-Methode aufgerufen wird.int
Objekten fehlen einfach Mutator-Methoden , aber die Semantik der Python-Zuweisung hängt nicht vom Typ abIn Python gibt es einen einfachen Weg zu wissen:
Unveränderlich:
Veränderlich:
Und:
Daher denke ich, dass die integrierte Funktion auch in Python unveränderlich ist.
Aber ich verstehe wirklich nicht, wie Float funktioniert:
Es ist so seltsam.
quelle
x = (1, 2)
und versuche dann zu mutierenx
, es ist nicht möglich. Eine Möglichkeit, die ich auf Mutabilität überprüft habe, besteht darinhash
, dass sie zumindest für die eingebauten Objekte funktioniert.hash(1)
hash('a')
hash((1, 2))
hash(True)
Alle arbeiten undhash([])
hash({})
hash({1, 2})
alle funktionieren nicht.hash()
funktioniert dies dann, wenn das Objekt eine__hash__()
Methode definiert , obwohl benutzerdefinierte Klassen im Allgemeinen veränderbar sind.hash
Methode 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.