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 Line2D
krę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 matplotlib
obiekt ( Figure
, Axes
itp) 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 Figure
obiektu, a następnie dodajemy Axes
obiekt do tej figury. Zauważ, że ax
i 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ść lines
i ax.lines
jest taka sama, bardzo ważne jest, aby zauważyć, że obiekt, do którego odwołuje się lines
zmienna, nie jest tym samym, co obiekt, do którego odwołuje się obiekt, do którego odwołuje się obiekt, ax.lines
co można zobaczyć w następujący sposób:
>>> id(lines), id(ax.lines)
(212754584, 211335288)
W konsekwencji usunięcie elementu z lines
nic nie robi dla bieżącego wykresu, ale usunięcie elementu z ax.lines
powoduje 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ś Line2D
obiekt 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ć Line2D
instancję 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.axes
tak samo dobrze, jak działaax.lines
Teraz prawdziwy problem. Jeśli będziemy przechowywać odniesienie zawarte w ax.lines[0]
do weakref.ref
obiektu, 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 Line2D
obiektu, na które wskazuje odniesienie wr
. Pamiętasz, jak lines
nie miał tego samego identyfikatora co, ax.lines
ale 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.