Это очень длинное объяснение, которое я напечатал для своего коллеги. Думаю, здесь тоже было бы полезно. Но будьте терпеливы. Я подхожу к реальной проблеме, с которой вы столкнулись ближе к концу. Как тизер, это проблема наличия дополнительных ссылок на ваши 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>
Итак, мораль этой истории такова: убирайте за собой. Если вы ожидаете, что что-то будет собрано сборщиком мусора, но это не так, вы, скорее всего, оставите где-то висеть ссылку.