Python:在交互模式下使用gc模块的不同行为

时间:2014-05-24 09:08:44

标签: python garbage-collection

我希望能够获得对类的任何现有对象实例的引用元组。我想出的是:

import gc

def instances(theClass):
    instances = []
    gc.collect()
    for i in gc.get_referrers(theClass):
        if isinstance(i, theClass):
            instances.append(i)
    return tuple(instances)

如果在Python intepreter提示符下输入上述代码,则可以执行以下操作:

>>> class MyClass(object):
>>>     pass

>>> c = MyClass()
>>> instances(MyClass)
(<__main__.MyClass object at 0x100c616d0>,)

万岁。但似乎gc.collect()似乎并没有在函数内部做任何事情:

>>> del c
>>> instances(MyClass)
(<__main__.MyClass object at 0x100c616d0>,)

gc.collect()在函数外部时起作用:

>>> del c
>>> gc.collect()
>>> instances(MyClass)
()

所以,我的问题是:如何在函数内部gc.collect()实际完成一个完整的集合(为什么它不能按原样运行)?带有推论的问题:是否有更好的方法来实现返回元组的相同目标,该元组具有对特定类的对象实例的引用?

注意:这都是在Python 2.7.3中尝试过的。我还没有在Python 3中尝试过它,但我的目标是拥有一个可以工作的功能(或者至少可以用2to3转换)。

编辑(下面的回答)澄清问题实际上是关于交互模式,而不是gc.collect()功能本身。

2 个答案:

答案 0 :(得分:4)

当你在交互模式下工作时,有一个神奇的内置变量_,它保存你运行的最后一个表达式语句的结果:

>>> 3 + 4
7
>>> _
7

当您删除c变量时,del c不是表达式,因此_不变:

>>> c = MyClass()
>>> instances(MyClass)
(<__main__.MyClass object at 0x00000000022E1748>,)
>>> del c
>>> _
(<__main__.MyClass object at 0x00000000022E1748>,)

_保留对MyClass实例的引用。当您致电gc.collect()时,这是一个表达式,因此gc.collect()的返回值会替换_的旧值,最后会收集c。它与垃圾收集器没有任何关系;任何表达式都可以:

>>> 4
4
>>> instances(MyClass)
()

答案 1 :(得分:1)

我认为有一种更简单,更可靠的方法来获取您想要的信息,而无需在gc中进行搜索:您可以让班级负责跟踪其实例。

这里我使用元类将实例列表附加到InstanceTracker的每个子类,并覆盖__new__以将每个创建的实例添加到列表中。 (这是Python 3代码,它需要适应一点才能使用Python 2.)

class InstanceTrackerMeta(type):
    def __new__(meta, name, bases, dct):
        cls = super().__new__(meta, name, bases, dct)
        cls.instances = []
        return cls

class InstanceTracker(metaclass=InstanceTrackerMeta):
    def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls, *args, **kwargs)
        cls.instances.append(instance)
        return instance


# subclass InstanceTracker to get a class which remembers its instances
class MyClass(InstanceTracker):
    pass

c = MyClass()
print(MyClass.instances)
# [<__main__.MyClass object at 0x107b9d9b0>]

注意:此代码可能需要调整,具体取决于您是否要跟踪子类的实例等。如果您希望在垃圾回收时删除实例,则需要覆盖__del__中的InstanceTracker。 如果您只需要跟踪系统中某个类的实例,您也可以简化它以消除元类。