Как я могу удалить линию (или строки) осей matplotlib таким образом, чтобы она фактически собирала мусор и освобождала память? Приведенный ниже код удаляет строку, но никогда не освобождает память (даже при явных вызовах gc.collect())
Ответ 2
Это очень длинное объяснение, которое я набрал для моего коллеги. Я думаю, что здесь было бы полезно. Будьте терпеливы. Я добираюсь до настоящего вопроса, который у вас есть к концу. Подобно тизеру, это вопрос о дополнительных ссылках на ваши объекты Line2D
, висящие вокруг.
ПРЕДУПРЕЖДЕНИЕ: Еще одно замечание перед тем, как мы погрузимся. Если вы используете IPython для проверки этого, IPython сохраняет ссылки самостоятельно, и не все из них являются слабыми. Таким образом, проверка сборки мусора в IPython не работает. Это просто путает вопросы.
Хорошо, здесь мы идем. Каждый объект matplotlib
(Figure
, Axes
и т.д.) Предоставляет доступ к его дочерним исполнителям через различные атрибуты. Следующий пример довольно длинный, но должен быть освещен.
Начнем с создания объекта Figure
, а затем добавим объект Axes
к этой фигуре. Обратите внимание, что ax
и fig.axes[0]
- это один и тот же объект (тот же id()
).
>>> #Create a figure
>>> fig = plt.figure()
>>> fig.axes
[]
>>> #Add an axes object
>>> ax = fig.add_subplot(1,1,1)
>>> #The object in ax is the same as the object in fig.axes[0], which is
>>> # a list of axes objects attached to fig
>>> print ax
Axes(0.125,0.1;0.775x0.8)
>>> print fig.axes[0]
Axes(0.125,0.1;0.775x0.8) #Same as "print ax"
>>> id(ax), id(fig.axes[0])
(212603664, 212603664) #Same ids => same objects
Это также распространяется на строки в объекте осей:
>>> #Add a line to ax
>>> lines = ax.plot(np.arange(1000))
>>> #Lines and ax.lines contain the same line2D instances
>>> 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)
>>> #Same ID => same object
>>> id(lines[0]), id(ax.lines[0])
(216550352, 216550352)
Если вы должны были вызвать plt.show()
, используя то, что было сделано выше, вы увидите фигуру, содержащую набор осей и одну строку:
![A figure containing a set of axes and a single line]()
Теперь, когда мы видели, что содержимое lines
и ax.lines
одно и то же, очень важно отметить, что объект, на который ссылается переменная lines
, не совпадает с объектом, почитаемым ax.lines
, как видно из следующего:
>>> id(lines), id(ax.lines)
(212754584, 211335288)
Как следствие, удаление элемента из lines
ничего не делает для текущего графика, но удаление элемента из ax.lines
удаляет эту строку из текущего графика. Итак:
>>> #THIS DOES NOTHING:
>>> lines.pop(0)
>>> #THIS REMOVES THE FIRST LINE:
>>> ax.lines.pop(0)
Итак, если бы вы выполнили вторую строку кода, вы удалили бы объект Line2D
, содержащийся в ax.lines[0]
, из текущего графика, и он исчезнет. Обратите внимание, что это также можно сделать с помощью ax.lines.remove()
, что означает, что вы можете сохранить экземпляр Line2D
в переменной, а затем передать его в ax.lines.remove()
, чтобы удалить эту строку, например:
>>> #Create a new line
>>> lines.append(ax.plot(np.arange(1000)/2.0))
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>, <matplotlib.lines.Line2D object at 0xce84dx3>]
![A figure containing a set of axes and two lines]()
>>> #Remove that new line
>>> ax.lines.remove(lines[0])
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84dx3>]
![A figure containing a set of axes and only the second line]()
Все вышеприведенные работы для fig.axes
так же хорошо, как и для ax.lines
Теперь, настоящая проблема здесь. Если мы сохраним ссылку, содержащуюся в ax.lines[0]
, в объект weakref.ref
, а затем попытаемся удалить ее, мы заметим, что она не получает сбор мусора:
>>> #Create weak reference to Line2D object
>>> 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>
>>> #Delete the line from the axes
>>> ax.lines.remove(wr())
>>> ax.lines
[]
>>> #Test weakref again
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>
Ссылка все еще живая! Зачем? Это связано с тем, что есть еще одна ссылка на объект Line2D
, на который указывает ссылка в wr
. Помните, как lines
не имел того же идентификатора, что и ax.lines
, но содержал те же элементы? Ну, эта проблема.
>>> #Print out 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.
>>> #Reinitialize lines to empty list
>>> lines = []
>>> print lines
[]
>>> print wr
<weakref at 0xb758af8; dead>
Итак, мораль этой истории, очистите себя. Если вы ожидаете, что что-то будет собирать мусор, но это не так, вы, вероятно, оставите где-нибудь ссылку.