2016-05-14 10 views
16

Ich möchte Objekte eines bestimmten Typs verfolgen, die gerade verwendet werden. Zum Beispiel: Verfolgen Sie alle Instanzen einer Klasse oder alle Klassen, die von einer Metaklasse erstellt wurden.In Python: Wie entferne ich ein Objekt von einer Liste, wenn es nur in dieser Liste referenziert wird?

Es ist leicht, wie diese Spur von Instanzen zu halten:

class A(): 
    instances = [] 
    def __init__(self): 
     self.instances.append(self) 

Aber wenn eine Instanz nicht irgendwo außerhalb dieser Liste verwiesen wird, es wird nicht mehr benötigt werden, und ich möchte nicht, dass die Instanz verarbeiten in eine möglicherweise zeitaufwendige Schleife.

Ich habe versucht, Objekte zu entfernen, die nur in der Liste mit sys.getrefcount referenziert werden.

Das Problem, das ich habe, ist, dass die Referenzzahl sehr unklar ist. Öffnen einer neuen Schale und eine Dummy-Klasse ohne Inhalt kehrt 5 Erstellung für

sys.getrefcount(DummyClass) 

Eine weitere Idee ist es, die Objekte dann das Löschen der Liste und Überprüfung zu kopieren, welche Objekte für Mülleinsammlungs und im letzten Schritt geplant Entfernen wurden diese Objekte. Etwas wie:

Copy = copy(A.instances) 
del A.instances 
A.instances = [i for i in Copy if not copy_of_i_is_in_GC(i)] 

Die Objekte müssen nicht sofort entfernt werden, wenn der Referenzzähler auf 0 geht ich einfach nicht zu viele Ressourcen auf Objekte verschwenden will, das nicht mehr verwendet werden.

+1

Verwenden Sie eine [schwache Referenz] (https://docs.python.org/3.5/library/weakref.html) – Barmar

+0

http://stackoverflow.com/questions/12101958/keep-track-of-instances-in -python –

+0

http://effbot.org/pyfaq/how-do-i-get-a-list-of-all-instances-of-a-given-class.htm –

Antwort

7

Diese Antwort ist die gleiche wie die von Kevin, aber ich arbeitete eine Beispielimplementierung mit schwachen Referenzen auf und poste sie hier. Die Verwendung schwacher Referenzen löst das Problem, bei dem ein Objekt von der self.instance-Liste referenziert wird, so dass es niemals gelöscht wird.

Einer der Gründe für das Erstellen einer schwachen Referenz für ein Objekt ist, dass Sie einen Rückruf einfügen können, wenn das Objekt gelöscht wird. Es gibt Probleme wie der Rückruf nicht passiert, wenn das Programm beendet wird ... aber das mag was Sie wollen sowieso.

import threading 
import weakref 

class A(object): 
    instances = [] 
    lock = threading.RLock() 

    @classmethod 
    def _cleanup_ref(cls, ref): 
     print('cleanup') # debug 
     with cls.lock: 
      try: 
       cls.instances.remove(ref) 
      except ValueError: 
       pass 

    def __init__(self): 
     with self.lock: 
      self.instances.append(weakref.ref(self, self._cleanup_ref)) 

# test 
test = [A() for _ in range(3)] 
for i in range(3,-1,-1): 
    assert len(A.instances) == i 
    if test: 
     test.pop() 

print("see if 3 are removed at exit") 
test = [A() for _ in range(3)] 
+0

In der Python-Shell (3.5.1) Die Assertion in der Testschleife schlägt fehl, da der Cleanup-Callback nicht aufgerufen wurde, wenn die Schleife die Assertion das zweite Mal erreicht. Alles, was vor der assert-Anweisung stdout ist, oder nach test.pop() behebt es. Wenn also "False" vor "assert" gesetzt wird, wird es repariert, während "None" es nicht repariert. – uzumaki

+0

@uzumaki interessant. Ich habe mit Python 3.4 getestet. Ich bin verwirrt, warum der Rückruf in 3.5 nicht passiert ist. – tdelaney

6

Der üblicher Weg, dieses Problem zu lösen, ist durch weak references. Die Grundidee besteht darin, dass Sie eine Liste schwacher Verweise auf Objekte anstelle der Objekte selbst führen und die toten schwachen Verweise regelmäßig aus der Liste entfernen.

Für Wörterbücher und Sätze gibt es einige abstraktere Typen wie weakref.WeakKeyDictionary(), die verwendet werden können, wenn Sie schwache Referenzen an komplexeren Stellen wie den Schlüsseln eines Wörterbuchs platzieren möchten. Diese Typen erfordern kein manuelles Beschneiden.

1

Dank @Barmar für den Hinweis auf WeakRef zu verwenden. Wir können es mit der __del__-Methode kombinieren, um eine selbstverwaltende Instanzliste einer Klasse zu implementieren.Somit kann die class A in OP Posten als erweitert:

from weakref import ref 
class A(): 
    instances = [] 
    def __init__(self): 
     self.instances.append(ref(self)) 

    @staticmethod 
    def __del__(): 
     if A: 
     A.instances = [i for i in A.instances if not i() is None] 

Testing

#python2.7 
print dict((len(A.instances), A()) for i in range(5)).keys() # 0,1,2,3,4 
print len(A.instances) # 0 

Die destructor __del__ kann als statische Methode oder ein Objekt gebundenen Verfahren wie def __del__(self): deklariert werden, obwohl dies nicht der Fall dokumentiert. Letzteres kann verhindern, dass das Objekt zerstört wird, indem ein anderer Verweis darauf erstellt wird. Hier verwende ich den statischen, da kein weiterer Verweis auf das sterbende Objekt benötigt wird. Der obige Code wird in Python 2.7 und 3.3 getestet.

Der Rückruf weakref.ref verhält sich ähnlich wie __del__, außer dass er an das Objekt "weakref" gebunden ist. Wenn Sie also mehrere Schwachstellen für das gleiche Objekt mit der gleichen Callback-Funktion erstellen, wird genau dieselbe Zeit wie die Anzahl der Schwachstellen aufgerufen.

+0

Beachten Sie, dass ['__del__'] (https://docs.python.org/3.5/reference/datamodel.html#object.__del__) keine statische Methode sein sollte. Auch "not i() is None" ist besser geschrieben als 'i() is not None'. Wie tdelaney zeigt, bietet "weakref.ref" bereits die Möglichkeit, einen Callback hinzuzufügen, der aufgerufen wird, wenn das Objekt zerstört wird. – Bakuriu

+0

@Bakuriu: Danke für den Kommentar, bitte sehen Sie die aktualisierte Antwort für die Diskussion über '__del__' und' weakref.ref' – gdlmx

Verwandte Themen