如何确定先前在我的Python程序中创建的对象是否仍然存在?通过存在,我的意思是,仍然可以通过我仍然持有参考的其他对象达到。
背景:
我正在尝试使用可能有问题的解析器。解析器返回一个具有递归引用的成员的对象。使用我在网上找到的任何调试转储命令都无法对对象进行pickle或dump。当我逐步完成解析器时,我可以看到一个我以后需要访问的对象。但它看起来好像对象最终没有在解析器返回的对象中实际引用。我想确定我看到在调试器中创建的对象是否在解析器返回的对象中的某个位置可用。
答案 0 :(得分:7)
gc
模块是调试此类信息的方法。例如:
import gc
a = [1, 2, 3]
b = [a, a]
gc.collect()
refs = gc.get_referrers(a)
我们知道a
变量本身就是指对象。或者,更准确地说,__main__
模块的全局变量在与键a
关联的值下引用该对象。但是有没有其他的参考?
print(len(refs))
print(refs)
这会打印2
。然后,除了打印模块的全局变量外,它还打印出[[1, 2, 3], [1, 2, 3]]
,b
的值。
所以,如果你知道在你正在寻找的对象旁边会有多少个对象的引用(在现实生活中比在一个简单的例子中要复杂得多),或者你有一些方法来识别你所拥有的价值试图检查,gc.get_referrers
可以做你想要的。如果你不知道其中任何一个,那么通过挑选整个引用列表并试图找出它,真的有办法解决这个问题,这会非常困难。
当然,另一种方法是提供一种功能,可以遍历您的数据结构并搜索您要查找的值。对于某些结构来说,这可能很困难甚至是不可能的,但是如果你可以这样做,那么通常值得做各种调试,而不仅仅是这个。
或者,当然,如果您可以向对象添加__del__
方法,则可以证明它已不再存在(通过记录对其__del__
的调用),但这不是帮助证明确实存在(它可能没有实时参照物,但尚未收集...实际上,只需添加__del__
可以阻止来自被收集,如果它在一个循环中)。
答案 1 :(得分:1)
这绝不是您应该在任何类型的生产设置中使用的解决方案(主要是因为它通常最终会引发段错误)但是出于调试目的,您可以使用以下代码片段来访问变量id以确定它是否仍然存在。 (来自here的片段)
>>> 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还可以返回一些其他对象(使用留在其中的垃圾构建)。即使在之后它也可以返回相同的对象也是可行的 如果其内存位置中的字节没有改变,则进行垃圾收集。我自己也没有目睹这一点,但我不明白为什么它不可能。
答案 2 :(得分:1)
您可以为对象创建weak reference,然后检查该引用是否仍然可以解决:
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')