To bardzo długie wyjaśnienie, które napisałem dla mojego współpracownika. Myślę, że również tutaj byłoby pomocne. Bądź jednak cierpliwy. Dochodzę do prawdziwego problemu, który masz pod koniec. Podobnie jak zwiastun, jest to kwestia dodatkowych odniesień do Line2Dkręcących się wokół obiektów.
OSTRZEŻENIE: Jeszcze jedna uwaga, zanim zaczniemy. Jeśli używasz IPythona do testowania tego, IPython zachowuje własne odwołania i nie wszystkie z nich są slabymi ref. Tak więc testowanie czyszczenia pamięci w IPythonie nie działa. Po prostu wprowadza zamieszanie.
Dobra, zaczynamy. Każdy matplotlibobiekt ( Figure, Axesitp) zapewnia dostęp do swoich dziecięcych artystów poprzez różne atrybuty. Poniższy przykład robi się dość długi, ale powinien być pouczający.
Zaczynamy od stworzenia Figureobiektu, a następnie dodajemy Axesobiekt do tej figury. Zauważ, że axi fig.axes[0]to ten sam obiekt (taki sam 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)
Dotyczy to również linii w obiekcie osi:
>>>
>>> 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)
Gdybyś miał wywołać plt.show()to, co zostało zrobione powyżej, zobaczyłbyś figurę zawierającą zestaw osi i jedną linię:

Teraz, chociaż widzieliśmy, że zawartość linesi ax.linesjest taka sama, bardzo ważne jest, aby zauważyć, że obiekt, do którego odwołuje się lineszmienna, nie jest tym samym, co obiekt, do którego odwołuje się obiekt, do którego odwołuje się obiekt, ax.linesco można zobaczyć w następujący sposób:
>>> id(lines), id(ax.lines)
(212754584, 211335288)
W konsekwencji usunięcie elementu z linesnic nie robi dla bieżącego wykresu, ale usunięcie elementu z ax.linespowoduje usunięcie tej linii z bieżącego wykresu. Więc:
>>>
>>> lines.pop(0)
>>>
>>> ax.lines.pop(0)
Tak więc, jeśli miałbyś uruchomić drugą linię kodu, usunąłbyś Line2Dobiekt zawarty w ax.lines[0]bieżącym wykresie i zniknąłby. Zauważ, że można to również zrobić poprzez ax.lines.remove()to, że możesz zapisać Line2Dinstancję w zmiennej, a następnie przekazać ją, ax.lines.remove()aby usunąć tę linię, na przykład:
>>>
>>> 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>]

Wszystko to działa fig.axestak samo dobrze, jak działaax.lines
Teraz prawdziwy problem. Jeśli będziemy przechowywać odniesienie zawarte w ax.lines[0]do weakref.refobiektu, a następnie próbuje go usunąć, to zauważymy, że nie zostanie śmieci zbierane:
>>>
>>> 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>
Odniesienie jest nadal aktywne! Czemu? Dzieje się tak, ponieważ istnieje jeszcze inne odniesienie do Line2Dobiektu, na które wskazuje odniesienie wr. Pamiętasz, jak linesnie miał tego samego identyfikatora co, ax.linesale zawierał te same elementy? Cóż, w tym problem.
>>>
>>> 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>
Więc morał tej historii jest taki, posprzątaj po sobie. Jeśli spodziewasz się, że coś zostanie zebrane jako śmieci, ale tak nie jest, prawdopodobnie zostawiasz gdzieś jakieś odniesienie.