如何删除闭包中捕获的变量 class
How to delete variable captured in closure class
看下面两个函数,第一个returns一个函数闭包,第二个"class closure"。 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
不存在。
所以我的问题是:为什么捕获的变量在class的情况下没有被删除;并且:如何在 Python 2.7 中删除它而不使用一些使用 weakref
的奇怪跟踪机制?
您无需执行任何操作。
CPython 结合使用引用计数和垃圾收集器来处理不需要的对象。在第一种情况下,删除带有 del a
的闭包会将泄漏对象的引用计数减少到 0,并立即将其处理掉。在第二种情况下,Inner
class、它的 __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
看下面两个函数,第一个returns一个函数闭包,第二个"class closure"。 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
不存在。
所以我的问题是:为什么捕获的变量在class的情况下没有被删除;并且:如何在 Python 2.7 中删除它而不使用一些使用 weakref
的奇怪跟踪机制?
您无需执行任何操作。
CPython 结合使用引用计数和垃圾收集器来处理不需要的对象。在第一种情况下,删除带有 del a
的闭包会将泄漏对象的引用计数减少到 0,并立即将其处理掉。在第二种情况下,Inner
class、它的 __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