我想跟踪当前正在使用的某种类型的对象。例如:跟踪一个类的所有实例或由元类创建的所有类。
很容易跟踪这样的实例:
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
我遇到的问题是引用计数非常模糊。 打开一个新shell并创建一个没有内容的虚拟类,返回5
sys.getrefcount(DummyClass)
另一个想法是复制对象然后删除列表并检查哪些对象已被安排用于垃圾收集,并在最后一步中删除这些对象。类似的东西:
Copy = copy(A.instances)
del A.instances
A.instances = [i for i in Copy if not copy_of_i_is_in_GC(i)]
当引用计数变为0时,不必立即删除对象。我只是不想在不再使用的对象上浪费太多的资源。
答案 0 :(得分:7)
解决此问题的标准方法是weak references。基本思想是保留一个对象的弱引用列表而不是对象本身,并定期从列表中删除死弱引用。
对于字典和集合,还有一些更抽象的类型,例如weakref.WeakKeyDictionary()
,当你想将弱引用放在更复杂的地方,如字典的键时,可以使用它们。这些类型不需要手动修剪。
答案 1 :(得分:7)
这个答案与凯文的答案相同,但我正在编写一个带有弱引用的示例实现,并在此处发布。使用弱引用解决了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)]
答案 2 :(得分:1)
试试gc.get_referrers(obj)
。 The gc
module Documentation
len(gc.get_referrers(my_obj))
答案 3 :(得分: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):
,尽管没有记录。后者可以通过创建对它的另一个引用来阻止对象被破坏。这里我使用静态的,因为不需要另一个对dieing对象的引用。上面的代码在Python 2.7和3.3中进行了测试。
weakref.ref
回调的行为类似于__del__
,除了它绑定到“weakref”对象。因此,如果为具有相同回调函数的同一对象创建多个weakref,则将与weakrefs的数量完全相同地调用它。