Esta es una explicación muy larga que escribí para un compañero de trabajo. Creo que aquí también sería útil. Pero tenga paciencia. Llego al problema real que está teniendo hacia el final. Solo como un adelanto, se trata de tener referencias adicionales a sus Line2D
objetos merodeando.
ADVERTENCIA: Una nota más antes de sumergirnos. Si está utilizando IPython para probar esto, IPython mantiene sus propias referencias y no todas son referencias débiles. Entonces, probar la recolección de basura en IPython no funciona. Simplemente confunde las cosas.
Bien, aquí vamos. Cada matplotlib
objeto ( Figure
, Axes
, etc.) proporciona acceso a sus niños artistas a través de varios atributos. El siguiente ejemplo se está volviendo bastante largo, pero debería ser esclarecedor.
Comenzamos creando un Figure
objeto, luego agregamos un Axes
objeto a esa figura. Tenga en cuenta que ax
y fig.axes[0]
son el mismo objeto (mismo id()
).
>>>
>>> fig = plt.figure()
>>> fig.axes
[]
>>>
>>> ax = fig.add_subplot(1,1,1)
>>>
>>>
>>> print ax
Axes(0.125,0.1;0.775x0.8)
>>> print fig.axes[0]
Axes(0.125,0.1;0.775x0.8)
>>> id(ax), id(fig.axes[0])
(212603664, 212603664)
Esto también se extiende a las líneas en un objeto de ejes:
>>>
>>> lines = ax.plot(np.arange(1000))
>>>
>>> print lines
[<matplotlib.lines.Line2D object at 0xce84bd0>]
>>> print ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>]
>>> print lines[0]
Line2D(_line0)
>>> print ax.lines[0]
Line2D(_line0)
>>>
>>> id(lines[0]), id(ax.lines[0])
(216550352, 216550352)
Si llamara plt.show()
usando lo que se hizo anteriormente, verá una figura que contiene un conjunto de ejes y una sola línea:
Ahora, si bien hemos visto que el contenido de lines
y ax.lines
es el mismo, es muy importante notar que el objeto al que hace referencia la lines
variable no es el mismo que el objeto reverenciado por ax.lines
como se puede ver en lo siguiente:
>>> id(lines), id(ax.lines)
(212754584, 211335288)
Como consecuencia, eliminar un elemento de lines
no afecta al gráfico actual, pero eliminar un elemento de ax.lines
elimina esa línea del gráfico actual. Entonces:
>>>
>>> lines.pop(0)
>>>
>>> ax.lines.pop(0)
Entonces, si tuviera que ejecutar la segunda línea de código, eliminaría el Line2D
objeto contenido en ax.lines[0]
el gráfico actual y desaparecería. Tenga en cuenta que esto también se puede hacer, ax.lines.remove()
lo que significa que puede guardar una Line2D
instancia en una variable, luego pasarla a ax.lines.remove()
para eliminar esa línea, así:
>>>
>>> lines.append(ax.plot(np.arange(1000)/2.0))
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>, <matplotlib.lines.Line2D object at 0xce84dx3>]
>>>
>>> ax.lines.remove(lines[0])
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84dx3>]
Todo lo anterior funciona fig.axes
tan bien como funciona paraax.lines
Ahora, el verdadero problema aquí. Si almacenamos la referencia contenida en ax.lines[0]
un weakref.ref
objeto, luego intentamos eliminarlo, notaremos que no se recolecta la basura:
>>>
>>> from weakref import ref
>>> wr = ref(ax.lines[0])
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>
>>>
>>> ax.lines.remove(wr())
>>> ax.lines
[]
>>>
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>
¡La referencia aún está viva! ¿Por qué? Esto se debe a que todavía hay otra referencia al Line2D
objeto al que wr
apunta la referencia . ¿Recuerda lines
que no tenía el mismo ID ax.lines
pero contenía los mismos elementos? Bueno, ese es el problema.
>>>
>>> print lines
[<matplotlib.lines.Line2D object at 0xce84bd0>, <matplotlib.lines.Line2D object at 0xce84dx3>]
To fix this problem, we simply need to delete `lines`, empty it, or let it go out of scope.
>>>
>>> lines = []
>>> print lines
[]
>>> print wr
<weakref at 0xb758af8; dead>
Entonces, la moraleja de la historia es, limpia después de ti mismo. Si espera que algo sea recolectado como basura pero no lo es, es probable que esté dejando una referencia en algún lugar.