Це дуже довге пояснення, яке я набрав для свого колеги. Думаю, це було б корисно і тут. Однак будьте терплячі. Я переходжу до реального питання, яке виникає у вас до кінця. Так само, як тизер, це питання наявності додаткових посилань на ваші Line2D
предмети, що висяться.
ПОПЕРЕДЖЕННЯ: Ще одна примітка перед тим, як ми зануримося. Якщо ви використовуєте IPython для перевірки цього, IPython зберігає власні посилання, і не всі вони є слабкими посиланнями. Отже, тестування збору сміття в IPython не працює. Це просто плутає справи.
Гаразд, ось ми йдемо. Кожен matplotlib
об'єкт ( Figure
, Axes
тощо) забезпечує доступ до своїх дочірніх виконавців за допомогою різних атрибутів. Наступний приклад стає досить довгим, але повинен бути яскравим.
Спочатку ми створюємо Figure
об’єкт, а потім додаємо Axes
об’єкт до цієї фігури. Зверніть увагу, що ax
і fig.axes[0]
є однаковим об'єктом (однаковим 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)
Це також поширюється на лінії в об'єкті осей:
>>>
>>> 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)
Якби ви зателефонували, plt.show()
використовуючи те, що було зроблено вище, ви побачите фігуру, що містить набір осей і один рядок:
Тепер, хоча ми переконались, що вміст lines
і ax.lines
є однаковим, дуже важливо відзначити, що об’єкт, на який посилається lines
змінна, не є таким, як об’єкт, на який поважають, ax.lines
як це можна побачити з наступного:
>>> id(lines), id(ax.lines)
(212754584, 211335288)
Як наслідок, видалення елемента з lines
нічого не робить для поточної ділянки, але видалення елемента з ax.lines
видаляє цю лінію з поточної ділянки. Тому:
>>>
>>> lines.pop(0)
>>>
>>> ax.lines.pop(0)
Отже, якщо вам потрібно було запустити другий рядок коду, ви вилучили б Line2D
об’єкт, що міститься в ньому, ax.lines[0]
з поточного сюжету, і його не було б. Зверніть увагу, що це також можна зробити через ax.lines.remove()
те, що ви можете зберегти Line2D
екземпляр у змінній, а потім передати його, ax.lines.remove()
щоб видалити цей рядок, наприклад:
>>>
>>> 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>]
Все вищезазначене працює fig.axes
так само добре, як і дляax.lines
Тепер справжня проблема тут. Якщо ми зберігаємо посилання , що міститься в ax.lines[0]
в weakref.ref
об'єкт, а потім спробувати видалити його, ми помітимо , що він не отримує сміття:
>>>
>>> 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>
Довідка ще живе! Чому? Це пов’язано з тим, що існує ще одне посилання на Line2D
об’єкт, на яке вказує посилання wr
. Пам’ятаєте, як lines
не було того самого ідентифікатора, що ax.lines
й ті, що містили ті самі елементи? Ну, в цьому проблема.
>>>
>>> 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>
Отже, мораль історії - прибирайте за собою. Якщо ви очікуєте, що щось буде зібраним сміттям, але це не так, ви, ймовірно, десь залишаєте посилання, що висить.