Solución:
los gc
módulo es la forma de depurar este tipo de información. Por ejemplo:
import gc
a = [1, 2, 3]
b = [a, a]
gc.collect()
refs = gc.get_referrers(a)
Conocemos el a
la variable en sí se refiere al objeto. O, más exactamente, los globales del __main__
El módulo se refiere a ese objeto, bajo el valor asociado con la clave. a
. ¿Pero hay otros árbitros?
print(len(refs))
print(refs)
Esto imprime 2
. Y luego, además de imprimir los globales del módulo, también imprime [[1, 2, 3], [1, 2, 3]]
, El valor de b
.
Entonces, si sabe cuántas referencias al objeto existirán junto al que está buscando (mucho más complicado en la vida real que en un ejemplo trivial), o si tiene alguna forma de identificar el valor que está tratando de verificar , gc.get_referrers
puede hacer lo que quiera. Si tu no Conozca cualquiera de esos, realmente hay una manera de resolverlo seleccionando toda la lista de referencias y tratando de resolverlo, lo que se vuelve muy difícil.
Una forma alternativa de hacer esto, por supuesto, es proporcionar una función que recorra sus estructuras de datos y busque el valor que estaba buscando. Esto puede ser difícil o incluso imposible para algunas estructuras, pero si pueden hágalo, a menudo vale la pena hacerlo por todo tipo de razones de depuración, no solo por esta.
O, por supuesto, si puede agregar un __del__
a su objeto, puede probar que ya no existe (registrando una llamada a su __del__
), pero eso no ayuda a probar que lo hace existen (puede que no tenga referentes en vivo, pero aún no se hayan recopilado … de hecho, solo agregue un __del__
mayo evitar que sea recolectado, si es en un ciclo).
Esta no es de ninguna manera una solución que deba usar en ningún tipo de configuración de producción (en gran parte porque generalmente termina arrojando una falla de segmento) pero para fines de depuración, puede usar el siguiente fragmento de código para acceder a una variable por su ID para determinar si todavía existe. (Fragmento de aquí)
>>> 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)
Esencialmente, la incapacidad de ctypes para devolver el objeto después de que se eliminó la última referencia implica que ya no existe.
Me estreso de nuevo sin embargo, este no es de ninguna manera un método de prueba infalible ni adecuado para un entorno de producción. Aparte de los segfaults, ctypes también podría devolver algún otro objeto (construido usando la basura que queda en su lugar). También es factible que pueda devolver el mismo objeto. incluso después
su recolección de basura si los bytes en su ubicación de memoria no han cambiado. No he sido testigo de esto por mí mismo, pero no veo por qué no sería posible.
Puede crear una referencia débil a un objeto y luego verificar si esa referencia aún se puede resolver:
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
Para una exploración en profundidad o lo que mantiene vivo su objeto, puede usar objgraph:
import objgraph
x = []
y = [x, [x], dict(x=x)]
objgraph.show_backrefs([x], filename="sample-backref-graph.png")