请看以下两个函数,第一个返回函数闭包,第二个返回“类闭包”。 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
永远不会被删除。在 Python 3 中可以通过使用 nonlocal
和 __del__
来修复此问题,但在 Python 2.7 中则不行,因为 nonlocal
不存在。
所以我的问题是:为什么在类的情况下捕获的变量没有被删除;以及:如何在 Python 2.7 中删除它而不使用 weakref
使用一些奇怪的跟踪机制?
最佳答案
您无需执行任何操作。
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
关于python - 如何删除闭包类中捕获的变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50172872/