C'est une explication très détaillée que j'ai rédigée pour un collègue à moi. Je pense qu'elle serait utile ici aussi. Soyez patient, cependant. J'aborde le vrai problème que vous rencontrez vers la fin. Juste pour vous donner un avant-goût, il s'agit d'un problème avec des références supplémentaires à vos objets Line2D
qui traînent.
ATTENTION : Une autre remarque avant de plonger. Si vous utilisez IPython pour tester cela, IPython garde ses propres références et toutes ne sont pas des weakrefs. Donc, tester la collecte de déchets dans IPython ne fonctionne pas. Cela complique juste les choses.
D'accord, allons-y. Chaque objet matplotlib
(Figure
, Axes
, etc.) donne accès à ses éléments enfants via divers attributs. L'exemple suivant devient assez long, mais devrait être éclairant.
Nous commençons par créer un objet Figure
, puis ajoutons un objet Axes
à cette figure. Notez que ax
et fig.axes[0]
sont le même objet (même id()
).
>>> #Créer une figure
>>> fig = plt.figure()
>>> fig.axes
[]
>>> #Ajouter un objet axes
>>> ax = fig.add_subplot(1,1,1)
>>> #L'objet dans ax est le même que l'objet dans fig.axes[0], qui est
>>> # une liste d'objets axes attachée à fig
>>> print ax
Axes(0.125,0.1;0.775x0.8)
>>> print fig.axes[0]
Axes(0.125,0.1;0.775x0.8) #Même que "print ax"
>>> id(ax), id(fig.axes[0])
(212603664, 212603664) #Mêmes ids => mêmes objets
Cela s'applique également aux lignes dans un objet axes :
>>> #Ajouter une ligne à ax
>>> lines = ax.plot(np.arange(1000))
>>> #Les lignes et ax.lines contiennent les mêmes instances Line2D
>>> print lines
[]
>>> print ax.lines
[]
>>> print lines[0]
Line2D(_line0)
>>> print ax.lines[0]
Line2D(_line0)
>>> #Même ID => même objet
>>> id(lines[0]), id(ax.lines[0])
(216550352, 216550352)
Si vous appelez plt.show()
avec ce qui a été fait ci-dessus, vous verrez une figure contenant un ensemble d'axes et une seule ligne :
Maintenant, bien que nous ayons vu que le contenu de lines
et de ax.lines
est le même, il est très important de noter que l'objet référencé par la variable lines
n'est pas le même que l'objet référencé par ax.lines
, comme on peut le voir ci-dessous :
>>> id(lines), id(ax.lines)
(212754584, 211335288)
Par conséquent, retirer un élément de lines
ne fait rien au tracé actuel, mais retirer un élément de ax.lines
supprime cette ligne du tracé actuel. Donc :
>>> #Cela NE FAIT RIEN :
>>> lines.pop(0)
>>> #CETTE LIGNE ÉLIMINE LA PREMIÈRE LIGNE :
>>> ax.lines.pop(0)
Ainsi, si vous exécutez la deuxième ligne de code, vous supprimez l'objet Line2D
contenu dans ax.lines[0]
du tracé actuel et il disparaîtra. Notez que cela peut également être fait via ax.lines.remove()
, ce qui signifie que vous pouvez enregistrer une instance Line2D
dans une variable, puis la passer à ax.lines.remove()
pour supprimer cette ligne, comme ceci :
>>> #Créer une nouvelle ligne
>>> lines.append(ax.plot(np.arange(1000)/2.0))
>>> ax.lines
[, ]
>>> #Supprimer cette nouvelle ligne
>>> ax.lines.remove(lines[0])
>>> ax.lines
[]
Tout ce qui précède fonctionne aussi bien pour fig.axes
que pour ax.lines
Maintenant, le vrai problème ici. Si nous stockons la référence contenue dans ax.lines[0]
dans un objet weakref.ref
, puis tentons de la supprimer, nous remarquerons qu'elle n'est pas collectée par le ramasse-miettes :
>>> #Créer une référence faible à l'objet Line2D
>>> from weakref import ref
>>> wr = ref(ax.lines[0])
>>> print wr
>>> print wr()
>>> #Supprimer la ligne des axes
>>> ax.lines.remove(wr())
>>> ax.lines
[]
>>> #Tester à nouveau la weakref
>>> print wr
>>> print wr()
La référence est toujours active! Pourquoi ? Parce qu'il y a encore une autre référence à l'objet Line2D
auquel la référence dans wr
pointe. Vous souvenez-vous comment lines
n'avait pas le même ID que ax.lines
>>> #Afficher lines
>>> print lines
[, ]
Pour résoudre ce problème, il suffit de supprimer `lines`, de le vider ou de le laisser sortir de la portée.
>>> #Réinitialiser lines pour le vider
>>> lines = []
>>> print lines
[]
>>> print wr
La morale de l'histoire est donc de bien ranger. Si vous vous attendez à ce que quelque chose soit ramassé par le ramasse-miettes mais que ce n'est pas le cas, vous laissez probablement une référence traîner quelque part.