2016-04-17 2 views
5

Also habe ich eine benutzerdefinierte Klasse in Python erstellt, die Iteration mit der __iter__ Methode unterstützt. Meine __iter__ Methode ist als Generatorobjekt strukturiert, wobei yield verwendet wird, um jeden der Werte in der Instanz der Klasse anzugeben.Der Aufruf von iter() für eine Instanz meiner Klasse erzeugt keinen "Schnappschuss" der aktuellen Werte?

Ich finde, dass, wenn ich so etwas wie x = iter(my_instance) sage und dann die Daten in meiner Instanz gespeichert bearbeiten, wenn ich anschließend eine Liste aus x bauen, werde ich feststellen, dass die Liste der Werte enthält, die my_class derzeit eher als die Werte, die es hatte, als ich iter() anrief.

Also meine Frage ist an dieser Stelle zweifach. Zuallererst, ist das was passieren soll? Wenn ja, warum funktioniert es so?


Oben ist die Hauptsache, aber wenn Sie wirklich darüber hinaus gehen wollen, lesen Sie weiter. Der Grund, warum ich diese Frage stelle, ist, dass ich diese Klasse als eine Aufgabe an der Universität erstellen musste. Mein Professor hat ein Programm zur Verfügung gestellt, das eine Reihe von Tests zu dieser Klasse durchführen wird, die ich erstellt habe, um mir eine gute Vorstellung davon zu geben, ob ich es beim ersten Mal richtig gemacht habe.

Mein Programm besteht jede einzelne Prüfung mit Ausnahme der letzten, die im Grunde das oben beschriebene tut. Das Problem ist, dass erwartet wird, dass die Liste, die aus einem vorherigen Aufruf von iter(my_object) erstellt wurde, nur die Werte widerspiegelt, die in my_object zu dem Zeitpunkt gespeichert wurden, zu dem iter() aufgerufen wurde.

Meine erste Reaktion, nachdem diese mit Blick auf war, dass mein Professor ähnlich ein Ergebnis kommen sollte einfach list(my_object) anrufen, aber ich bin der Student hier, so scheint es viel wahrscheinlicher, ich einfach in einige sehr seltsam, meine __iter__ Methode kodiert Weg, der dieses Verhalten verursacht. Oder hat Python geändert, wie die iter() Funktion in letzter Zeit funktioniert?


Ich würde es vorziehen, nicht meinen Code zu schreiben, da es für eine Klasse ist, und dass technisch betrachtet werden könnte betrüge (ich weiß, das ist dumm und wenig hilfreich, sorry). Allerdings kann ich die Spezifikation verknüpfen, die richtig ist here (speziell Teil 1, die Bag Klasse).

+1

Ihr Iterator und Ihre Klasse verwenden dasselbe zugrunde liegende Listenobjekt für den Speicher. Aus diesem Grund sehen beide Änderungen, wenn Sie die Daten ändern. – Nayuki

+0

Wenn Sie den Iterator "einfrieren" möchten, können Sie ihn explizit in eine Liste (oder einen anderen konsumierenden Datentyp) konvertieren. Iteratoren sind normalerweise faul, was erklärt, warum Sie das beschriebene Verhalten haben. –

+1

Wenn Sie über veränderbare Objekte iterieren (und Ihr Bag ist veränderbar), werden auch Iteratoren aus der Standardbibliothek das gleiche Verhalten zeigen: Sie werden auf dem iterablen im aktuellen Zustand operieren, was Modifikationen widerspiegelt. – Cyb3rFly3r

Antwort

1

Die Zuordnung erscheint Ihnen einen starken Hinweis darauf zu geben, wie Sie diesen

Stellen Sie sicher zu gehen, dass der Iterator diese Werte in der Tasche zu der Zeit erzeugt der Iterator Ausführung beginnt; Das Mutieren des Beutels während der Iteration beeinflusst nicht, welche Werte er erzeugt.

Hinweis: Schreiben Sie diese Methode als Aufruf an einen lokalen Generator und übergeben Sie eine Kopie des Wörterbuchs (behandelt in der Vorlesung am Freitag in Woche 4).

+0

Oh man, ich habe mir den zweiten Teil davon angesehen und dachte, es bedeute 'erzeuge einen Generator' und verpasse das erste Bit. Danke Kumpel! – LandGod

2

As (glaube ich) @ Cyb3rFly3r zu sagen versucht, das Verhalten über eine veränderbare Klasse von iterieren, die dies mit der Art und Weise hoch diskordanten tut, ist es für die Container-Klassen in der Standardbibliothek arbeitet - und damit wäre eine etwas umstrittene Design-Wahl, um auf diese Gründe allein in der realen Welt zu machen.

Es widerspricht wahrscheinlich auch einer der Hauptgründe, warum das Konzept der Iteratoren in Python eingeführt wurde, nämlich das Erstellen separater Kopien des Inhalts von Containerobjekten, so dass sie nicht wiederholt werden konnten.

Wie auch immer, das ist genau das, was Sie in Ihrer __iter__() Methode tun müssen (und klingt wie das, was von Detail in der Andeutung 11 der linked specification in Peter Gibson's answer gezeigt ist darauf hindeutet, Kopieren das Wörterbuch, die alle Informationen speichert in einer Bag Instanz - sei es eine Ebene dict ionary, eine defaultdict, oder was auch immer).

Sie könnten Dinge optimieren (durch den zusätzlichen Speicher für die Kopie benötigt wird, minimiert und die Leichtigkeit der intern Iterieren davon) durch einen internen list seines Inhalts zu schaffen, anstatt das Erstellen einer Kopie des gesamten wahrscheinlich mehr komplexen Daten Struktur. ** Tipp **: Auf diese Weise würde wahrscheinlich sehr ähnlich sein, was Sie die __repr__() Methode implementieren müssen tun werden, um die Ergebnisse wie folgt aussehen:

        Tasche ([ ‚a‘, "c", "b", "b", "d", "d", "d"])

wie in Detail 3 der Spezifikation gezeigt.

Da Sie sich entschieden haben, keinen Code zu posten, ist es schwierig, Ihnen viel weiter zu helfen.

+0

Okay, das macht jetzt Sinn. Nachdem ich das gelesen habe und was Perter Gibson geschrieben hat, macht alles einen Sinn. Ich dachte nur, dass wir gefragt werden müssen, Generator-Aussagen zu verwenden, da wir das gelernt haben, wurde ich irgendwie von der Idee überrascht, Dinge auf eine weniger effiziente Art und Weise zu machen. – LandGod

+1

Sie können immer noch das verwenden, was Sie Generatoranweisungen in Ihrer Implementierung nennen - nur sie werden über die Kopie oder alternative Datenstruktur iterieren, die Sie verwendet haben, um das gewünschte Verhalten zu erreichen. – martineau

+0

Ja, genau das habe ich getan. – LandGod