Warum eine gebundene Methode auf Python-Objekt setzen, um einen Zirkelverweis zu erstellen?

8

Ich arbeite in Python 2.7 und ich mochte dieses Problem, das mich verwirrt.

Das ist das einfachste Beispiel:

%Vor%

Das ist OK wie erwartet ... jetzt versuche ich, die Methode a() des Objekts a zu ändern und was passiert ist, dass ich nach der Änderung% ce_de% nicht mehr löschen kann:

%Vor%

Nur um einige Überprüfungen durchzuführen, habe ich die a Referenz vor und nach der Zuweisung ausgedruckt

%Vor%

Schließlich habe ich a.a Modul verwendet, um zu verstehen, warum das Objekt nicht freigegeben wurde:

%Vor%

%Vor%

Wie Sie im objgraph image sehen können, gibt es post-backref-graph.png Referenzen in b, die für mich keinen Sinn ergeben, da die Selbstreferenzen der Instanzmethode ignoriert werden sollten (wie vor der Zuweisung).

Jemand kann erklären, warum dieses Verhalten und wie kann ich es umgehen?

    
Michele d'Amico 02.10.2014, 09:27
quelle

3 Antworten

3

Wenn Sie a.a schreiben, wird effektiv Folgendes ausgeführt:

%Vor%

, weil Sie nicht auf eine vorgebundene Methode, sondern auf die Klassenmethode zugreifen Zur Laufzeit gebunden.

Wenn Sie

tun %Vor%

Sie "cachen" effektiv den Akt des Bindens der Methode. Da die gebundene Methode eine Referenz auf das Objekt hat (offensichtlich, da sie self an die Funktion übergeben muss), erzeugt dies eine zirkuläre Referenz.

Also modelliere ich dein Problem wie folgt:

%Vor%

Sie können schwache Referenzen verwenden, um bei Bedarf in lof_all_calls like:

zu binden %Vor%

Das ist wirklich hässlich, also würde ich es lieber herausziehen, um einen weakmethod Dekorator zu erstellen:

%Vor%

Fertig!

FWIW, Python 3.4 hat nicht nur diese Probleme, es hat auch WeakMethod für Sie vorgefertigt.

    
Veedrac 02.10.2014, 09:37
quelle
4

Vedracs Antwort über die gebundene Methode, die einen Verweis auf die Instanz enthält, ist nur ein Teil der Antwort. CPythons Garbage Collector weiß, wie man zyklische Referenzen erkennt und behandelt - außer wenn ein Objekt, das Teil des Zyklus ist, eine __del__ -Methode hat, wie hier erwähnt Ссылка :

  

Objekte, die __del__() Methoden haben und Teil eines Referenzzyklus sind   verursachen, dass der gesamte Referenzzyklus nicht eingreifbar ist, einschließlich   Objekte nicht unbedingt im Zyklus, sondern nur von dort erreichbar.   Python sammelt solche Zyklen nicht automatisch, da im Allgemeinen   es ist Python nicht möglich, eine sichere Reihenfolge zu erraten, in der der Befehl ausgeführt werden soll    __del__() Methoden. (...) Es ist im Allgemeinen besser, das Problem zu vermeiden, indem Sie keine Zyklen erstellen, die Objekte mit __del__() Methoden enthalten, und   Müll kann in diesem Fall untersucht werden, um zu verifizieren, dass solche Zyklen nicht vorliegen   erstellt werden.

IOW: Entfernen Sie Ihre __del__ Methode und Sie sollten in Ordnung sein.

EDIT: wrt / Ihr Kommentar:

  

Ich verwende es für das Objekt als Funktion a.a = functor(a.a) . Wenn der Test   Ist fertig Ich möchte den Funktor durch die ursprüngliche Methode ersetzen.

Dann ist die Lösung einfach und einfach:

%Vor%

Bis Sie es explizit binden, hat a kein 'a' Instanzattribut, also wird es nach der Klasse gesucht und eine neue method Instanz wird zurückgegeben (cf Ссылка für mehr dazu). Diese method Instanz wird dann aufgerufen und (normalerweise) verworfen.

    
bruno desthuilliers 02.10.2014 09:56
quelle
1

Warum Python das macht. Technisch gesehen enthalten Objekte all zirkuläre Referenzen, wenn sie Methoden haben. Die Garbage Collection würde jedoch wesentlich länger dauern, wenn der Garbage Collector eine explizite Überprüfung von Objektmethoden durchführen müsste, um sicherzustellen, dass das Objekt nicht beschädigt wird. Daher speichert Python die Methoden separat vom __dict__ eines Objekts. Wenn Sie also a.a = a.a schreiben, überschatten Sie die Methode mit sich selbst im Feld a des Objekts. Daher gibt es einen expliziten Verweis auf die Methode, die verhindert, dass das Objekt ordnungsgemäß freigegeben wird.

Die Lösung Ihres Problems besteht nicht darin, einen "Cache" der ursprünglichen Methode beizubehalten und die schattierte Variable einfach zu löschen, wenn Sie damit fertig sind. Dadurch wird die Methode ausgeblendet und wieder verfügbar gemacht.

%Vor%

Hier zeigt vars an, was sich im Attribut __dict__ eines Objekts befindet. Beachten Sie, dass __dict__ keinen Verweis auf sich selbst enthält, obwohl a.__dict__ gültig ist. dir erzeugt eine Liste aller Attribute, die von dem gegebenen Objekt erreichbar sind. Hier können wir alle Attribute und Methoden eines Objekts und alle Methoden und Attribute seiner Klassen und ihrer Basen sehen. Dies zeigt, dass die gebundene Methode von a separat an der Stelle gespeichert wird, an der a 's Attribute gespeichert sind.

    
Dunes 03.10.2014 13:36
quelle

Tags und Links