Warum können Tupel veränderbare Gegenstände enthalten?

178

Wenn ein Tupel unveränderlich ist, warum kann es dann veränderbare Elemente enthalten?

Es scheint ein Widerspruch zu sein, dass das Tupel, zu dem es gehört, unveränderlich bleibt, wenn ein veränderliches Element wie eine Liste geändert wird.

qazwsx
quelle

Antworten:

205

Das ist eine ausgezeichnete Frage.

Die wichtigste Erkenntnis ist, dass Tupel nicht wissen können, ob die Objekte in ihnen veränderlich sind. Das einzige, was ein Objekt veränderlich macht, ist eine Methode, die seine Daten ändert. Im Allgemeinen gibt es keine Möglichkeit, dies zu erkennen.

Eine weitere Erkenntnis ist, dass Pythons Container eigentlich nichts enthalten. Stattdessen behalten sie Verweise auf andere Objekte bei. Ebenso sind Pythons Variablen nicht wie Variablen in kompilierten Sprachen. Stattdessen sind die Variablennamen nur Schlüssel in einem Namespace-Wörterbuch, in denen sie einem entsprechenden Objekt zugeordnet sind. Ned Batchhelder erklärt dies in seinem Blog-Beitrag . In beiden Fällen kennen Objekte nur ihre Referenzanzahl. Sie wissen nicht, was diese Referenzen sind (Variablen, Container oder die Python-Interna).

Zusammen erklären diese beiden Erkenntnisse Ihr Rätsel (warum sich ein unveränderliches Tupel, das eine Liste "enthält", zu ändern scheint, wenn sich die zugrunde liegende Liste ändert). Tatsächlich hat sich das Tupel nicht geändert (es enthält immer noch dieselben Verweise auf andere Objekte wie zuvor). Das Tupel konnte sich nicht ändern (da es keine Mutationsmethoden hatte). Wenn sich die Liste geändert hat, wurde das Tupel nicht über die Änderung informiert (die Liste weiß nicht, ob auf sie von einer Variablen, einem Tupel oder einer anderen Liste verwiesen wird).

Während wir uns mit dem Thema befassen, sind hier einige andere Gedanken, die Ihnen helfen sollen, Ihr mentales Modell dessen zu vervollständigen, was Tupel sind, wie sie funktionieren und wie sie verwendet werden sollen:

  1. Tupel zeichnen sich weniger durch ihre Unveränderlichkeit als vielmehr durch ihren Verwendungszweck aus.
    Tupel sind Pythons Methode, heterogene Informationen unter einem Dach zu sammeln. Bringt beispielsweise s = ('www.python.org', 80) eine Zeichenfolge und eine Zahl zusammen, sodass das Host / Port-Paar als Socket, als zusammengesetztes Objekt, weitergegeben werden kann. In diesem Licht ist es durchaus vernünftig, veränderbare Komponenten zu haben.

  2. Unveränderlichkeit geht Hand in Hand mit einer anderen Eigenschaft, der Hashbarkeit . Aber Hashability ist keine absolute Eigenschaft. Wenn eine der Komponenten des Tupels nicht hashbar ist, ist auch das gesamte Tupel nicht hashbar. Zum Beispiel t = ('red', [10, 20, 30])ist nicht hashbar.

Das letzte Beispiel zeigt ein 2-Tupel, das eine Zeichenfolge und eine Liste enthält. Das Tupel selbst ist nicht veränderbar (dh es gibt keine Methoden zum Ändern seines Inhalts). Ebenso ist die Zeichenfolge unveränderlich, da Zeichenfolgen keine Mutationsmethoden haben. Das Listenobjekt verfügt über Mutationsmethoden, sodass es geändert werden kann. Dies zeigt, dass die Veränderbarkeit eine Eigenschaft eines Objekttyps ist - einige Objekte verfügen über Mutationsmethoden, andere nicht. Dies ändert sich nicht nur, weil die Objekte verschachtelt sind.

Denken Sie an zwei Dinge. Erstens ist Unveränderlichkeit keine Magie - es ist lediglich das Fehlen mutierender Methoden. Zweitens wissen Objekte nicht, auf welche Variablen oder Container sie verweisen - sie kennen nur die Referenzanzahl.

Hoffe, das war nützlich für dich :-)

Raymond Hettinger
quelle
Ist es nicht falsch? "Tupel können nicht wissen, ob die Objekte in ihnen veränderlich sind." Wir können feststellen, ob die Referenz eine Hash-Methode implementiert, dann ist sie unveränderlich. Wie ein Dic oder Set würde tun. Wäre dies nicht eher eine Designentscheidung für die Tupel?
Garg10Mai
@ garg10may 1) Hashibility ist ohne Aufruf nicht einfach zu erkennen, hash()da alles, was von object () erbt, hashbar ist und Unterklassen das Hashing explizit deaktivieren müssen. 2) Hashibility garantiert keine Unveränderlichkeit - es ist einfach, Beispiele für veränderbare Hash-Objekte zu erstellen. 3) Tupel haben, wie die meisten Container in Python, nur Verweise auf das zugrunde liegende Objekt - sie sind nicht verpflichtet, sie zu überprüfen und Rückschlüsse auf sie zu ziehen.
Raymond Hettinger
171

Das liegt an Tupeln keine Listen, Zeichenfolgen oder Zahlen enthalten. Sie enthalten Verweise auf andere Objekte . 1 Die Unfähigkeit, die in einem Tupel enthaltene Referenzsequenz zu ändern, bedeutet nicht, dass Sie die mit diesen Referenzen verknüpften Objekte nicht mutieren können. 2

1. Objekte, Werte und Typen (siehe: vorletzter Absatz)
2. Die Standardtyphierarchie (siehe: "Unveränderliche Sequenzen")

Ignacio Vazquez-Abrams
quelle
8
Die Frage ist mehrdeutig. Diese Antwort vollständig erklärt , warum es ist möglich für Tupeln veränderbare Objekte enthalten. Es erklärt nicht, warum Tupel so konzipiert wurden , dass sie veränderbare Objekte enthalten. Ich denke, letzteres ist die sachdienlichere Frage.
senderle
16

Erstens kann das Wort "unveränderlich" für verschiedene Menschen viele verschiedene Dinge bedeuten. Mir gefällt besonders, wie Eric Lippert Unveränderlichkeit in seinem Blogbeitrag kategorisiert hat . Dort listet er folgende Arten von Unveränderlichkeit auf:

  • Realio-Trulio-Unveränderlichkeit
  • Unveränderlichkeit einmal schreiben
  • Unveränderlichkeit des Eis am Stiel
  • Flach gegen tiefe Unveränderlichkeit
  • Unveränderliche Fassaden
  • Beobachtungsveränderlichkeit

Diese können auf verschiedene Arten kombiniert werden, um noch mehr Arten von Unveränderlichkeit zu erzielen, und ich bin sicher, dass es noch mehr gibt. Die Art der Unveränderlichkeit, an der Sie anscheinend an einer tiefen (auch als transitiven) Unveränderlichkeit interessiert sind, bei der unveränderliche Objekte nur andere unveränderliche Objekte enthalten können.

Der entscheidende Punkt dabei ist, dass tiefe Unveränderlichkeit nur eine von vielen, vielen Arten von Unveränderlichkeit ist. Sie können jede Art wählen, die Sie bevorzugen, solange Sie wissen, dass sich Ihre Vorstellung von "unveränderlich" wahrscheinlich von der Vorstellung eines anderen von "unveränderlich" unterscheidet.

Ken Wayne VanderLinde
quelle
Welche Unveränderlichkeit haben Python-Tupel?
Qazwsx
3
Python-Tupel haben eine geringe (auch als nicht transitiv bezeichnete) Unveränderlichkeit.
Ken Wayne VanderLinde
16

Nach meinem Verständnis muss diese Frage als Frage zu Entwurfsentscheidungen umformuliert werden: Warum haben die Designer von Python einen unveränderlichen Sequenztyp erstellt, der veränderbare Objekte enthalten kann?

Um diese Frage zu beantworten, müssen wir darüber nachdenken , den Zweck Tupeln dienen: sie dienen als schnell , Allzweck- Sequenzen. In diesem Sinne wird deutlich, warum Tupel unveränderlich sind, aber veränderbare Objekte enthalten können. Nämlich:

  1. Tupel sind schnell und speichereffizient: Tupel lassen sich schneller erstellen als Listen, da sie unveränderlich sind. Unveränderlichkeit bedeutet, dass Tupel als Konstanten erstellt und als solche geladen werden können, indem sie konstant gefaltet werden . Dies bedeutet auch, dass sie schneller und speichereffizienter zu erstellen sind, da keine Gesamtzuordnung usw. erforderlich ist. Sie sind etwas langsamer als Listen für den zufälligen Elementzugriff , aber beim Entpacken wieder schneller (zumindest auf meinem Computer). Wenn Tupel veränderlich wären, wären sie für solche Zwecke nicht so schnell.

  2. Tupel sind universell einsetzbar : Tupel müssen in der Lage sein, alle Arten von Objekten zu enthalten. Sie werden verwendet, um (schnell) Dinge wie Argumentlisten mit variabler Länge (über den *Operator in Funktionsdefinitionen) auszuführen. Wenn Tupel keine veränderlichen Objekte enthalten könnten, wären sie für solche Dinge nutzlos. Python müsste Listen verwenden, was wahrscheinlich die Dinge verlangsamen würde und sicherlich weniger speichereffizient wäre.

Sie sehen also, um ihren Zweck zu erfüllen, müssen Tupel unveränderlich sein, aber auch veränderbare Objekte enthalten können. Wenn die Designer von Python ein unveränderliches Objekt erstellen möchten, das garantiert, dass alle darin enthaltenen Objekte auch unveränderlich sind, müssten sie einen dritten Sequenztyp erstellen. Der Gewinn ist die zusätzliche Komplexität nicht wert.

senderle
quelle
12

Sie können die idElemente nicht ändern . Es wird also immer die gleichen Elemente enthalten.

$ python
>>> t = (1, [2, 3])
>>> id(t[1])
12371368
>>> t[1].append(4)
>>> id(t[1])
12371368
kev
quelle
Dies ist die am besten geeignete Demonstration der obigen Beispiele. Tupel hat Verweise auf Objekte, die sich nicht ändern, obwohl höchstens eine veränderbare Komponente das gesamte Tupel nicht zerlegbar macht.
Piepi
5

Ich werde hier auf die Nerven gehen und sagen, dass der relevante Teil hier darin besteht, dass Sie zwar den Inhalt einer Liste oder den Status eines in einem Tupel enthaltenen Objekts ändern können , das Objekt jedoch nicht ändern können oder Liste ist da. Wenn Sie etwas hatten, das davon abhängt, dass etwas [3] eine Liste ist, auch wenn es leer ist, dann könnte ich sehen, dass dies nützlich ist.


quelle
3

Ein Grund dafür ist, dass es in Python keine allgemeine Möglichkeit gibt, einen veränderlichen Typ in einen unveränderlichen Typ umzuwandeln (siehe den abgelehnten PEP 351 und die verknüpfte Diskussion warum er abgelehnt wurde). Daher wäre es unmöglich, verschiedene Arten von Objekten in Tupel zu setzen, wenn diese Einschränkung vorhanden wäre, einschließlich nahezu aller vom Benutzer erstellten nicht hashbaren Objekte.

Der einzige Grund, warum Wörterbücher und Mengen diese Einschränkung haben, besteht darin, dass die Objekte hashbar sein müssen, da sie intern als Hash-Tabellen implementiert sind. Beachten Sie jedoch, dass Wörterbücher und Mengen ironischerweise nicht unveränderlich (oder hashbar) sind. Tupel verwenden nicht den Hash eines Objekts, daher spielt seine Veränderlichkeit keine Rolle.

asmeurer
quelle
2

Ein Tupel ist in dem Sinne unveränderlich, dass sich das Tupel selbst nicht ausdehnen oder verkleinern kann, nicht dass alle darin enthaltenen Elemente unveränderlich sind. Ansonsten sind Tupel langweilig.

Adam Smith
quelle