如何确定之前在我的 Python 程序中创建的对象是否仍然存在?存在,我的意思是,仍然可以通过我仍然持有参考的其他对象访问。
背景:
我正在尝试使用可能有问题的解析器。解析器返回一个对象,其成员具有递归引用。无法使用我在网上找到的任何调试转储命令来腌制或转储该对象。当我单步执行解析器时,我可以看到创建了一个稍后需要访问的对象。但在我看来,对象并没有真正在解析器返回的对象中被引用。我想确定我看到在调试器中创建的对象是否在解析器返回的对象中的某处可用。
如何确定之前在我的 Python 程序中创建的对象是否仍然存在?存在,我的意思是,仍然可以通过我仍然持有参考的其他对象访问。
背景:
我正在尝试使用可能有问题的解析器。解析器返回一个对象,其成员具有递归引用。无法使用我在网上找到的任何调试转储命令来腌制或转储该对象。当我单步执行解析器时,我可以看到创建了一个稍后需要访问的对象。但在我看来,对象并没有真正在解析器返回的对象中被引用。我想确定我看到在调试器中创建的对象是否在解析器返回的对象中的某处可用。
该gc
模块是调试此类信息的方式。例如:
import gc
a = [1, 2, 3]
b = [a, a]
gc.collect()
refs = gc.get_referrers(a)
我们知道a
变量本身是指对象。或者,更准确地说,__main__
模块的全局变量在与 key 关联的值下引用了该对象a
。但是还有其他裁判吗?
print(len(refs))
print(refs)
这打印2
。然后,除了打印模块的全局变量外,它还打印出[[1, 2, 3], [1, 2, 3]]
的值b
。
因此,如果您知道在您要查找的对象旁边存在多少对对象的引用(在现实生活中比在一个简单的示例中要复杂得多),或者您有某种方法来识别您要检查的值,gc.get_referrers
可以为所欲为。如果您不知道其中任何一个,那么通过挑选整个推荐人列表并尝试找出它,确实有办法找出它,这变得非常困难。
当然,另一种方法是提供一个函数来遍历您的数据结构并搜索您正在寻找的值。对于某些结构来说,这可能是困难的,甚至是不可能的,但如果你能做到,出于各种调试原因,通常值得这样做,而不仅仅是这个。
或者,当然,如果您可以__del__
向您的对象添加一个方法,您可以证明它不再存在(通过记录对其的调用__del__
),但这无助于证明它确实存在(它可能没有实时引用,但还没有被收集……事实上,如果它在一个循环中,只需添加一个__del__
可能会阻止它被收集)。
这绝不是您应该在任何类型的生产设置中使用的解决方案,(主要是因为它通常最终会引发段错误)但出于调试目的,您可以使用以下代码片段通过其 id 访问变量来确定如果它仍然存在。(来自这里的片段)
>>> import ctypes, gc
>>> a = "hello world"
>>> id(a)
7696579801024
>>> len(gc.get_referrers(a))
1
>>> li = [a]
>>> len(gc.get_referrers(a))
2
>>> del a
>>> print ctypes.cast(7696579801024, ctypes.py_object).value
hello world
>>> len(gc.get_referrers(ctypes.cast(7696579801024, ctypes.py_object).value))
1
>>> del li
>>> print ctypes.cast(7696579801024, ctypes.py_object).value
Segmentation fault (core dumped)
本质上,ctypes 在最后一个引用者被删除后无法返回对象意味着它不再存在。
不过,我再次强调,这绝不是一种万无一失的测试方法,也不适合生产环境。除了段错误,ctypes 还可以返回一些其他对象(使用留在其位置的垃圾构造)。如果其内存位置中的字节未更改,即使在 其垃圾收集之后它也可以返回相同的对象也是可行的。我自己没有亲眼目睹这一点,但我不明白为什么这是不可能的。
您可以创建对对象的弱引用,然后检查该引用是否仍然可以解析:
import weakref
class Foo:
pass
foo = Foo()
foo_ref = weakref.ref(foo)
print(foo_ref()) # Prints something like: <__main__.Foo instance at 0x7f7180895a28>
del foo
print(foo_ref()) # Prints: None
对于深入探索或使您的对象保持活力的原因,您可以使用objgraph:
import objgraph
x = []
y = [x, [x], dict(x=x)]
objgraph.show_backrefs([x], filename='sample-backref-graph.png')