如何删除在闭包类

时间:2018-05-04 10:34:00

标签: python scoping

请参阅以下两个函数,第一个函数返回一个函数闭包,第二个函数返回一个"类闭包"。 objects用于跟踪创建的对象。在这两种情况下,闭包都会捕获MyObject的实例。

import weakref

class MyObject(object):
    pass

def leak1():
    obj = MyObject()
    objects[id(obj)] = weakref.ref(obj)

    def inner():
        return obj

    return inner

def leak2():
    obj = MyObject()
    objects[id(obj)] = weakref.ref(obj)

    class Inner(object):

        __slots__ = () # edit

        def __call__(self):
            return obj

        #def __del__(self):
        #   nonlocal obj
        #   del obj

    return Inner()

def print_all_objects(s):
    for id, ref in objects.items():
        print(s, id, ref())

for leak in (leak1, leak2):
    print(leak.__name__)
    objects = {}
    a = leak()
    print_all_objects(1)
    del a
    print_all_objects(2)

如果你运行它,你会得到以下输出:

leak1
(1, 54150256L, <__main__.MyObject object at 0x00000000033A4470>)
(2, 54150256L, None)
leak2
(1, 54150256L, <__main__.MyObject object at 0x00000000033A4470>)
(2, 54150256L, <__main__.MyObject object at 0x00000000033A4470>)

这意味着在第一种情况下删除函数闭包后删除obj(这是我的预期)。 但在第二种情况下,永远不会删除obj。这可以使用nonlocal__del__在Python 3中修复,但在Python 2.7中则不行,因为nonlocal不存在。

所以我的问题是:为什么在类的情况下不删除捕获的变量;并且:如何使用weakref

在不使用某些奇怪的跟踪机制的情况下在Python 2.7中删除它

1 个答案:

答案 0 :(得分:3)

您无需做任何事情。

CPython使用引用计数和垃圾收集器的组合来处理不需要的对象。在第一种情况下,使用del a删除闭包会将泄漏对象的引用计数减少到0并立即处理。在第二种情况下,Inner类,其__call__方法和obj之间存在参考周期。此引用循环可防止引用计数降为0,因此不会立即删除闭包。但是一旦垃圾收集器开始其下一个收集周期,关闭处理掉。

如果要立即删除闭包,可以使用gc.collect()手动触发垃圾回收:

import gc

for leak in (leak1, leak2):
    print(leak.__name__)
    objects = {}
    a = leak()
    print_all_objects(1)
    del a
    gc.collect()  # <- add this
    print_all_objects(2)

输出:

leak1
1 140591616726800 <__main__.MyObject object at 0x7fde095f9710>
2 140591616726800 None
leak2
1 140591619339880 <__main__.MyObject object at 0x7fde09877668>
2 140591619339880 None