在Python中,如何卸载生成的类?

1 投票
2 回答
772 浏览
提问于 2025-04-15 11:53

我正在开发一个库,用来把文件(hfd5 - pytables)加载到一个对象结构中。这个结构中实际使用的类是从hdf5文件中以字符串的形式加载的,然后以这种方式进行加载:

class NamespaceHolder(dict):
    # stmt is the source code holding all the class defs
    def execute(self, stmt):
        exec stmt in self

问题是,像这样加载多个类会导致对象出现在垃圾回收的不可回收部分,也就是实际的类定义。我也可以把这些类加载到一个全局字典里,但孤立的类问题依然存在。有没有办法卸载这些类呢?

主要的问题在于类的mro属性,它包含了对类自身的引用,这就导致了循环引用,而垃圾回收器无法处理这种情况。

这里有一个小的测试案例,大家可以自己看看:

import gc

if __name__ == "__main__":
    gc.enable()
    gc.set_debug(gc.DEBUG_LEAK)

    code = """
class DummyA(object):
    pass
"""
    context = {}

    exec code in context
    exec code in context

    gc.collect()
    print len(gc.garbage)

顺便提一下:我之前已经反对过从文件中解析文本来创建类,但显然他们在这里坚持使用这种方法,并且看到了一些我看不到的好处,所以现在不可能放弃这个解决方案。

2 个回答

1

我觉得垃圾回收(GC)可以处理循环引用,不过你需要做的就是从globals()字典中移除这个引用:

try:
    del globals()['DummyA']
except KeyError:
    pass

否则会有一个非循环的引用指向这个类对象,这样就会导致它无法被清理掉。

1

gc.set_debug(gc.DEBUG_LEAK) 这个设置会导致内存泄漏。你可以试试这个:

import gc

def foo():                              
    code = """
class DummyA(object):
    pass             
"""
    context = {}
    exec code in context
    exec code in context

    gc.collect()
    print len(gc.garbage), len(gc.get_objects())

gc.enable()
foo(); foo() # amount of objects doesn't increase
gc.set_debug(gc.DEBUG_LEAK)
foo() # leaks

撰写回答