如何删除在闭包类中捕获的变量

2024-04-28 09:19:45 发布

您现在位置:Python中文网/ 问答频道 /正文

看下面两个函数,第一个返回函数闭包,第二个返回“类闭包”。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)

如果运行此命令,将得到以下输出:

^{pr2}$

这意味着在第一种情况下,obj在函数闭包被删除之后被删除(这是我所期望的)。 但是在第二种情况下,obj永远不会被删除。在python3中,可以通过使用nonlocal__del__来修复,但在python2.7中不能这样做,因为nonlocal不存在。在

所以我的问题是:为什么在类的情况下捕获的变量没有被删除;以及:如何在python2.7中删除它,而不使用一些奇怪的跟踪机制使用weakref?在


Tags: 函数refidobjreturnobjectsdef情况
1条回答
网友
1楼 · 发布于 2024-04-28 09:19:45

你什么都不用做。在

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

如果要立即删除闭包,可以使用^{}手动触发垃圾回收:

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)

输出:

^{pr2}$

相关问题 更多 >