Ich möchte in der Lage sein, Instanzen von geometrischen Point-Objekten zu verfolgen, um zu wissen, welche Namen bereits vergeben sind, wenn automatisch ein neuer Name vergeben wird.
Wenn beispielsweise die Punkte "A", "B" und "C" erstellt wurden, wird der nächste automatisch benannte Punkt "D" genannt. Wenn Punkt namens "D" gelöscht wird oder seine Referenz verloren geht, wird der Name "D" wieder verfügbar.
Die Hauptattribute meiner Point
-Objekte sind als Eigenschaften definiert und sind die Standardwerte x
, y
und name
.
Ich ging wie beschrieben hier weiter, indem ich weakref.WeakSet()
verwendete. Ich habe das zu meiner Klasse Point
hinzugefügt:
Problem ist, wenn ich einen Punkt installiere und dann lösche, wird es meistens, aber nicht immer , von Point.instances
entfernt. Ich habe festgestellt, dass, wenn ich die Testsuite ( pytest -x -vv -r w
) und dann wenn eine bestimmte Ausnahme im Test ausgelöst wird , die Instanz nie gelöscht wird (wahrscheinliche Erklärung etwas unten zu lesen).
Im folgenden Testcode wird nach dem ersten Löschen von p
immer von Point.instances
entfernt, aber nach dem zweiten Löschen von p
wird es nie gelöscht (Testergebnisse sind immer gleich) und Die letzte assert
-Anweisung schlägt fehl:
Und hier das Ergebnis:
%Vor%Der Code, der in der catched-Ausnahme getestet wurde, erstellt jedoch keine neue Point-Instanz:
%Vor%und Koordinaten sind grundsätzlich:
%Vor% wobei _x
und _y
grundsätzlich Zahlen enthalten.
Der Grund scheint zu sein (Zitat aus pythons Dokument ):
CPython-Implementierungsdetail: Es ist möglich, dass ein Referenzzyklus verhindert, dass die Referenzzählung eines Objekts auf Null geht. In diesem Fall wird der Zyklus später vom zyklischen Garbage Collector erkannt und gelöscht. Eine häufige Ursache für Referenzzyklen ist, wenn eine Ausnahme in einer lokalen Variablen abgefangen wurde.
Hinzufügen dieser Methode zu Point
class:
und die Verwendung von myPoint.untrack()
vor del myPoint
(oder vor dem Verlust der Referenz auf den Point auf andere Weise) scheint das Problem zu lösen.
Aber das ist ziemlich schwer, jedes Mal untrack()
aufrufen zu müssen ... in meinen Tests gibt es viele Punkte, die ich "tracken" muss, nur um sicherzustellen, dass alle Namen verfügbar sind, zum Beispiel.
Gibt es eine bessere Möglichkeit, diese Instanzen zu verfolgen? (entweder durch Verbesserung der hier verwendeten Tracking-Methode oder durch ein anderes besseres Mittel).
Versuchen Sie nicht, verfügbare Namen basierend auf allen Point
-Objekten zu verfolgen, die im gesamten Programm vorhanden sind. Vorhersagen, welche Objekte existieren werden und wann Objekte aufhören werden, ist schwierig und unnötig und wird sich bei verschiedenen Python-Implementierungen sehr unterschiedlich verhalten.
Erstens, warum versuchen Sie, die Einzigartigkeit von Punktnamen überhaupt durchzusetzen? Wenn Sie beispielsweise in einem Fenster eine Figur zeichnen und nicht zwei Punkte mit demselben Label in der gleichen Figur möchten, dann lassen Sie die Figur die Punkte darin verfolgen und einen neuen Punkt mit einem genommenen Namen ablehnen. Dies erleichtert auch das explizite Entfernen von Punkten aus einer Figur oder zwei Zahlen mit unabhängigen Punktnamen. Es gibt eine Reihe anderer Kontexte, in denen ein ähnliches explizites Container-Objekt sinnvoll sein kann.
Wenn es sich um frei fließende Punkte handelt, die keiner Geometrieumgebung zugeordnet sind, warum sollten Sie sie überhaupt benennen? Wenn ich einen Punkt bei (3.5, 2.4) darstellen möchte, ist es mir egal, ob ich es A oder B oder Bob nenne, und ich will sicher keinen Absturz, weil irgendein anderer Code irgendwo auf halbem Wege entschieden hat, anzurufen ihr Punkt Bob auch. Warum sind Namen oder Namenskonflikte wichtig?
Ich weiß nicht, was Ihr Anwendungsfall ist, aber für die meisten kann ich mir vorstellen, dass es am besten wäre, entweder die Namenseindeutigkeit innerhalb eines expliziten Containers zu erzwingen oder die Namenseindeutigkeit überhaupt nicht zu erzwingen.
Tags und Links python python-3.x py.test weak-references