2014-01-15 48 views
0

有人問了一個類似的問題:[Printing all instances of a class]。 儘管我不太關心打印它們,但我更想知道當前有多少實例「活着」。 捕獲此實例的原因更像是設置預定作業,每小時檢查這些「實時」未處理實例並豐富數據。之後,在這個實例中設置一個標誌或者直接刪除這個實例。 Torsten Marek在[question]中的回答:Printing all instances of a class使用weakrefs需要針對此類型的每個類調用基類構造函數,是否可以自動執行此操作?或者我們可以用其他方法獲取所有實例?如何獲取python中某個類的所有實例?

回答

1

當然,將計一類屬性:

class CountedMixin(object): 
    count = 0 
    def __init__(self, *args, **kwargs): 
     type(self).count += 1 
     super().__init__(*args, **kwargs) 
    def __del__(self): 
     type(self).count -= 1 
     try: 
      super().__del__() 
     except AttributeError: 
      pass 

你可以把這個稍微更神奇與裝飾或元類比一個基類,或者更簡單的,如果它可以是一個有點不太一般(我試圖讓它適合任何合理的多重繼承層次結構中的任何地方,這通常不需要擔心......),但基本上,這就是它的全部。

如果你想擁有這些實例本身(或更好,weakrefs他們),而不僅僅是他們的計數,只是instances=set()取代count=0,然後做instances.add(self)代替count += 1等。(此外,雖然,你可能要一個weakrefself,而不是self

+2

也許值得一提:與任何依賴於'__del__'方法,這可能會導致非直觀的行爲。例如:'CountedMixin()'使'CountedMixin.count'增加一段時間,即使新對象不可訪問。 –

+0

'__del__'也可以防止循環垃圾回收器在這些對象上工作 –

+0

@gnibbler:是的,如果你想創建對象的循環並且掛鉤它們的銷燬,你需要添加你自己的循環斷路器到'gc.callbacks' 。 – abarnert

4

您可以跟蹤它自己(見其他答案),或要求垃圾收集器:

import gc 

class Foo(object): 
    pass 

foo1, foo2 = Foo(), Foo() 

foocount = sum(1 for o in gc.get_referrers(Foo) if o.__class__ is Foo) 

這可能是近親如果你有很多對象,那麼速度就會變慢,但它通常不會太壞,而且它的好處是你可以很容易地與別人的代碼一起使用。

如果你想讓它趕上subclasess爲好,改變if子句中的發電機表達:... if isinstance(o, Foo))

注意:用於o.__class__而不是type(o)所以它適用於老式類。

+0

+1]。這對於每小時一次檢查來說很可能沒有問題,並且沒有使用'__del__'造成的副作用 –

0

如果你只希望這對CPython的工作,你的「活」的定義可以稍微鬆懈,還有另一種方式來做到這一點,可能對調試/內省目的有用:

>>> import gc 
>>> class Foo(object): pass 
>>> spam, eggs = Foo(), Foo() 
>>> foos = [obj for obj in gc.get_objects() if isinstance(obj, Foo)] 
>>> foos 
[<__main__.Foo at 0x1153f0190>, <__main__.Foo at 0x1153f0210>] 
>>> del spam 
>>> foos = [obj for obj in gc.get_objects() if isinstance(obj, Foo)] 
>>> foos 
[<__main__.Foo at 0x1153f0190>, <__main__.Foo at 0x1153f0210>] 
>>> del foos 
>>> foos = [obj for obj in gc.get_objects() if isinstance(obj, Foo)] 
>>> foos 
[<__main__.Foo at 0x1153f0190>] 

請注意,刪除spam實際上並沒有使其無法生效,因爲我們仍然參照foos中的同一對象。並且重新分配foos並沒有幫助,因爲顯然在發佈舊版本之前發生了對get_objects的調用。但最終,一旦我們停止提及它,它就會消失。

解決此問題的唯一方法是使用weakrefs。

當然,在一個大系統中,這個速度可能會非常慢,有或沒有弱點。

0

我不能kindall的答案發表評論,因此,我寫我的批評是對的答案:

gc.get_referrers(<ClassName>)的解決方案並不在Python 3 gc.get_referrers(<ClassName>)不返回任何類實例的方法與繼承的類工作這是繼承自<ClassName>

相反,您需要使用gc.get_objects(),因爲它返回完整的對象列表,所以速度要慢很多。但是在單元測試的情況下,你只是想確保你的對象在測試後被刪除(沒有循環引用),它應該足夠快。

也請不要忘記在檢查實例的數量之前先調用gc.collect(),以確保所有未引用的實例都被真正刪除。

我還看到一個弱引用的問題,這也是以這種方式計算的。弱引用的問題是,被引用的對象可能不再存在,因此isinstance(Instance, Class)可能會失敗,並顯示關於不存在的弱引用的錯誤。

下面是一個簡單的代碼示例:

import gc 

def getInstances(Class): 
    gc.collect() 
    Number = 0 
    InstanceList = gc.get_objects() 
    for Instance in InstanceList: 
    if 'weakproxy' not in str(type(Instance)): # avoid weak references 
     if isinstance(Instance, Class): 
     Number += 1 

    return Number 
相關問題