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



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

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解释器提示符下输入的,那么你可以这样做:

>>> 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()功能本身。

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

>>> 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)
()

我认为有一种更简单,更可靠的方法来获得您想要的信息,而无需在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>]

注意:这段代码可能需要调整,这取决于您是否想要跟踪子类的实例等等。如果您希望实例在垃圾收集时被删除,则需要在InstanceTracker中重写__del__。如果您只需要跟踪系统中一个类的实例,您也可以简化它以摆脱元类。

最新更新