2016-05-14 42 views
16

我想跟蹤當前正在使用的某種類型的對象。例如:跟蹤一個類的所有實例或所有由元類創建的類。在Python中:如果僅在列表中引用對象,如何從列表中刪除對象?

這是很容易跟蹤的實例是這樣的:

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

但是,如果一個實例是不是該列表將不再被需要之外的任何引用,我不想處理該實例中一個潛在的耗時循環。

我試圖刪除僅使用sys.getrefcount在列表中引用的對象。

for i in A.instances: 
    if sys.getrefcount(i) <=3: # in the list, in the loop and in getrefcount 
     # collect and remove after the loop 

我遇到的問題是引用計數很模糊。 打開一個新的外殼和

sys.getrefcount(DummyClass) 

另一個想法是複製的對象,然後刪除該對象已定於垃圾收集,並在最後一步刪除列表,並檢查創建一個沒有內容的回報5僞類那些對象。喜歡的東西:

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

的對象不必立即取出時引用計數爲0。我只是不想浪費對不再使用的對象太多ressources。

+1

使用[弱引用](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 –

回答

7

這個答案和Kevin的一樣,但我正在做一個弱引用的示例實現,並在這裏發佈。使用弱引用解決了由self.instance列表引用對象的問題,因此它永遠不會被刪除。

爲對象創建弱引用的一件事是,您可以在刪除對象時包含回調。有些問題,例如程序退出時不會發生回調......但這可能是您想要的。

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

在Python shell(3.5.1)中,測試循環中的斷言失敗,因爲循環第二次到達斷言時尚未調用清除回調。在assert語句之前或test.pop()修復它之前,將任何內容打印到標準輸出。因此,在「assert」之前放置「False」會修復它,而放置「None」不會修復它。 – uzumaki

+0

@uzumaki有趣。我用Python 3.4進行了測試。我很困惑爲什麼3.5的回調沒有發生。 – tdelaney

6

解決這個問題的標準方式是通過weak references。基本思想是,你保留一個弱對象的引用列表,而不是對象本身,並週期性地修剪列表中的弱引用。

對於字典和集合,還有一些更抽象的類型,如weakref.WeakKeyDictionary(),當您想要在更復雜的地方(如字典的鍵)中使用弱引用時可以使用它。這些類型不需要手動修剪。

1

感謝@Barmar您指出使用weakref。我們可以將它與__del__方法結合起來,實現一個類的自我管理實例列表。因此,在OP的帖子的class A可以擴展爲:

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] 

測試

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

析構函數__del__可以聲明爲靜態方法或類似def __del__(self):一個綁定對象的方法,儘管它不是記錄。後者可以通過創建另一個引用來阻止對象被銷燬。在這裏,我使用靜態對象,因爲不需要對死亡對象進行另一個引用。上面的代碼在Python 2.7和3.3中進行了測試。

weakref.ref回調行爲類似於__del__,只是它被綁定到「weakref」對象。因此,如果您爲具有相同回調函數的同一對象創建多個weakrefs,則它將與weakref的數量同時被調用。

+0

請注意['__del__'](https://docs.python.org/3.5/reference/datamodel.html#object.__del__)不應該是一個靜態方法。另外'不是我()是無'最好寫爲'我()不是無「。此外,正如tdelaney所示,'weakref.ref'已經提供了在對象被銷燬時添加調用的方法。 – Bakuriu

+0

@Bakuriu:感謝您的評論,請參閱關於'__del__'和'weakref.ref'的討論的最新答案。 – gdlmx