在 python pdb 下,当在 __del__ 方法中到达断点时,如果 gc 是原因,堆栈会是什么样子?
Under python pdb when a breakpoint is reached in a __del__ method, what would the stack look like if gc is the cause?
情况:我在 __del__
方法中放置了一个断点。当使用 del
显式删除对象时,堆栈很清楚 del
是调用 __del__
的原因。
但是,如果由于 out-of-scope/garbage 收集而调用了 __del__
,堆栈会是什么样子?是否清楚 gc
在堆栈上,或者它看起来像执行 gc
时代码所在的任何点?
或者这在其他方面是荒谬的?
经过一些基础研究,至少对于 CPython(由 Anaconda 生成)的答案如下,基于下面的脚本及其附带的输出。基本上,当范围丢失时收集对象时,堆栈将指向实例化帧的行,在 return 处收集对象。 IE。如果在从 afunc
调用 return 之后收集了一个对象,则 __del__
调用帧将位于调用 afunc
的位置。
虽然我无法重现示例,但推测如果发生延迟收集,__del__
的调用帧将在延迟收集发生的点。
示例脚本:
import traceback
class ClassWithDel:
def __init__(self, whoami='Default'):
self._whoami = whoami
def __del__(self):
print(f'{self._whoami}: In __del__')
traceback.print_stack()
def whoami(self):
print(f'I am {self._whoami}')
def afunc():
cwd = ClassWithDel()
cwd.whoami()
def closure():
cwd = ClassWithDel()
def closure_with_cwd():
cwd.whoami()
return closure_with_cwd
def afunc_with_closure():
closure_cwd = closure()
closure_cwd()
def afunc_overwrite():
cwd = ClassWithDel()
cwd.whoami()
cwd = ClassWithDel('NotDefault')
cwd.whoami()
def return_list():
results = [ClassWithDel('l1'), ClassWithDel('l2')]
return results
def afunc_overwrite_list():
l = return_list()
l2 = l[1]
l = l2
if __name__ == '__main__':
print('Starting with afunc...')
afunc()
print('\nStarting with closure...')
afunc_with_closure()
print('\nStarting with overwrite...')
afunc_overwrite()
print('\nStarting list overwrite...')
afunc_overwrite_list()
结果:
$ python -m gc_test
Starting with afunc...
I am Default
Default: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 56, in <module>
afunc()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
Starting with closure...
I am Default
Default: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 58, in <module>
afunc_with_closure()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
Starting with overwrite...
I am Default
Default: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 60, in <module>
afunc_overwrite()
File "gc_test.py", line 39, in afunc_overwrite
cwd = ClassWithDel('NotDefault')
File "gc_test.py", line 11, in __del__
traceback.print_stack()
I am NotDefault
NotDefault: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 60, in <module>
afunc_overwrite()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
Starting list overwrite...
l1: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 62, in <module>
afunc_overwrite_list()
File "gc_test.py", line 51, in afunc_overwrite_list
l = l2
File "gc_test.py", line 11, in __del__
traceback.print_stack()
l2: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 62, in <module>
afunc_overwrite_list()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
情况:我在 __del__
方法中放置了一个断点。当使用 del
显式删除对象时,堆栈很清楚 del
是调用 __del__
的原因。
但是,如果由于 out-of-scope/garbage 收集而调用了 __del__
,堆栈会是什么样子?是否清楚 gc
在堆栈上,或者它看起来像执行 gc
时代码所在的任何点?
或者这在其他方面是荒谬的?
经过一些基础研究,至少对于 CPython(由 Anaconda 生成)的答案如下,基于下面的脚本及其附带的输出。基本上,当范围丢失时收集对象时,堆栈将指向实例化帧的行,在 return 处收集对象。 IE。如果在从 afunc
调用 return 之后收集了一个对象,则 __del__
调用帧将位于调用 afunc
的位置。
虽然我无法重现示例,但推测如果发生延迟收集,__del__
的调用帧将在延迟收集发生的点。
示例脚本:
import traceback
class ClassWithDel:
def __init__(self, whoami='Default'):
self._whoami = whoami
def __del__(self):
print(f'{self._whoami}: In __del__')
traceback.print_stack()
def whoami(self):
print(f'I am {self._whoami}')
def afunc():
cwd = ClassWithDel()
cwd.whoami()
def closure():
cwd = ClassWithDel()
def closure_with_cwd():
cwd.whoami()
return closure_with_cwd
def afunc_with_closure():
closure_cwd = closure()
closure_cwd()
def afunc_overwrite():
cwd = ClassWithDel()
cwd.whoami()
cwd = ClassWithDel('NotDefault')
cwd.whoami()
def return_list():
results = [ClassWithDel('l1'), ClassWithDel('l2')]
return results
def afunc_overwrite_list():
l = return_list()
l2 = l[1]
l = l2
if __name__ == '__main__':
print('Starting with afunc...')
afunc()
print('\nStarting with closure...')
afunc_with_closure()
print('\nStarting with overwrite...')
afunc_overwrite()
print('\nStarting list overwrite...')
afunc_overwrite_list()
结果:
$ python -m gc_test
Starting with afunc...
I am Default
Default: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 56, in <module>
afunc()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
Starting with closure...
I am Default
Default: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 58, in <module>
afunc_with_closure()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
Starting with overwrite...
I am Default
Default: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 60, in <module>
afunc_overwrite()
File "gc_test.py", line 39, in afunc_overwrite
cwd = ClassWithDel('NotDefault')
File "gc_test.py", line 11, in __del__
traceback.print_stack()
I am NotDefault
NotDefault: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 60, in <module>
afunc_overwrite()
File "gc_test.py", line 11, in __del__
traceback.print_stack()
Starting list overwrite...
l1: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 62, in <module>
afunc_overwrite_list()
File "gc_test.py", line 51, in afunc_overwrite_list
l = l2
File "gc_test.py", line 11, in __del__
traceback.print_stack()
l2: In __del__
File ".../lib/python3.8/runpy.py", line 194, in _run_module_as_main
return _run_code(code, main_globals, None,
File ".../lib/python3.8/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "gc_test.py", line 62, in <module>
afunc_overwrite_list()
File "gc_test.py", line 11, in __del__
traceback.print_stack()