Quelle est la méthode __del__, comment l'appeler?

109

Je lis un code. Il existe une classe dans laquelle la __del__méthode est définie. J'ai compris que cette méthode est utilisée pour détruire une instance de la classe. Cependant, je ne trouve pas d'endroit où cette méthode est utilisée. La principale raison est que je ne sais pas comment cette méthode est utilisée, probablement pas comme ça: obj1.del(). Alors, ma question est de savoir comment appeler la __del__méthode?

Verrtex
la source

Réponses:

168

__del__est un finaliseur . Il est appelé lorsqu'un objet est garbage collection, ce qui se produit à un moment donné après que toutes les références à l'objet ont été supprimées.

Dans un cas simple, cela peut être juste après que vous ayez dit del xou, s'il xs'agit d'une variable locale, après la fin de la fonction. En particulier, à moins qu'il y ait des références circulaires, CPython (l'implémentation standard de Python) effectuera immédiatement une récupération de place.

Cependant, il s'agit d'un détail d'implémentation de CPython. La seule propriété requise du garbage collection Python est que cela se produit après que toutes les références ont été supprimées, donc cela peut ne pas nécessairement se produire juste après et peut ne pas se produire du tout .

De plus, les variables peuvent vivre longtemps pour de nombreuses raisons , par exemple, une exception de propagation ou une introspection de module peut maintenir le nombre de références de variables supérieur à 0. En outre, la variable peut faire partie du cycle de références - CPython avec garbage collection activé interrompt le plus , mais pas tous, de tels cycles, et même alors seulement périodiquement.

Puisque vous n'avez aucune garantie qu'il soit exécuté, il ne faut jamais mettre le code dans lequel vous devez être exécuté __del__()- à la place, ce code appartient à la finallyclause du trybloc ou à un gestionnaire de contexte dans une withinstruction. Cependant, il existe des cas d'utilisation valides pour __del__: par exemple, si un objet Xréférence Yet conserve également une copie de Yréférence dans un global cache( cache['X -> Y'] = Y), il serait alors poli de X.__del__supprimer également l'entrée du cache.

Si vous savez que le destructor fournit (en violation de la directive ci - dessus) un nettoyage nécessaire, vous voudrez peut - être appeler directement , depuis à ce sujet il n'y a rien de spécial comme méthode: x.__del__(). Évidemment, vous ne devriez le faire que si vous savez que cela ne vous dérange pas d'être appelé deux fois. Ou, en dernier recours, vous pouvez redéfinir cette méthode en utilisant

type(x).__del__ = my_safe_cleanup_method  
ilya n.
la source
5
Vous dites que la fonctionnalité de CPython de supprimer un objet immédiatement après que son nombre de références est réduit à zéro est un "détail d'implémentation". Je ne suis pas convaincu. Pouvez-vous fournir un lien pour sauvegarder cette affirmation? (Je veux dire, la police en gras est assez convaincante en elle-même, mais les liens sont une seconde proche ... :-)
Stuart Berg
14
Détail de l'implémentation de CPython: CPython utilise actuellement un schéma de comptage de références avec détection retardée (facultative) des déchets liés cycliquement, ... D'autres implémentations agissent différemment et CPython peut changer. ( docs.python.org/2/reference/datamodel.html )
ilya n.
Quoi de neuf __exit__dans ce contexte? Est-il exécuté après ou avant __del__ou ensemble?
lony
1
Est-ce que «peut ne pas se produire du tout» inclut la fin du programme?
Andy Hayden
1
@AndyHayden: les __del__méthodes peuvent ne pas s'exécuter même à la fin du programme, et même lorsqu'elles s'exécutent à la fin, l'écriture d'une __del__méthode qui fonctionne correctement même lorsque l'interpréteur est occupé à s'autodétruire autour de vous nécessite un codage plus soigneux que ce que de nombreux programmeurs appliquent. (Le nettoyage CPython obtient généralement des __del__méthodes à exécuter à l'arrêt de l'interpréteur, mais il y a encore des cas où cela ne suffit pas. Les threads de démon, les globaux de niveau C et les objets __del__créés dans un autre __del__peuvent tous conduire à des __del__méthodes qui ne s'exécutent pas.)
user2357112 prend en charge Monica
80

J'ai rédigé la réponse à une autre question, bien que ce soit une question plus précise pour cela.

Comment fonctionnent les constructeurs et les destructeurs?

Voici une réponse légèrement opiniâtre.

N'utilisez pas __del__. Ce n'est pas du C ++ ou un langage conçu pour les destructeurs. La __del__méthode devrait vraiment être supprimée dans Python 3.x, même si je suis sûr que quelqu'un trouvera un cas d'utilisation qui a du sens. Si vous devez l'utiliser __del__, tenez compte des limitations de base par http://docs.python.org/reference/datamodel.html :

  • __del__est appelée lorsque le garbage collector collecte les objets, pas lorsque vous perdez la dernière référence à un objet et non lorsque vous exécutez del object.
  • __del__est responsable d'appeler n'importe lequel __del__dans une superclasse, bien qu'il ne soit pas clair si c'est dans l'ordre de résolution de méthode (MRO) ou simplement appeler chaque superclasse.
  • Avoir un __del__signifie que le ramasse-miettes abandonne la détection et le nettoyage des liens cycliques, comme la perte de la dernière référence à une liste liée. Vous pouvez obtenir une liste des objets ignorés à partir de gc.garbage. Vous pouvez parfois utiliser des références faibles pour éviter complètement le cycle. Cela est débattu de temps en temps: voir http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • le __del__ fonction peut tricher, enregistrer une référence à un objet et arrêter le garbage collection.
  • Les exceptions explicitement levées dans __del__sont ignorées.
  • __del__complète __new__bien plus que __init__. Cela devient déroutant. Voir http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del -is-not-the-opposé-of- init / pour une explication et des pièges.
  • __del__n'est pas un enfant "bien-aimé" en Python. Vous remarquerez que la documentation de sys.exit () ne spécifie pas si les déchets sont collectés avant de quitter, et il y a beaucoup de problèmes étranges. L'appel de __del__on globals provoque des problèmes d'ordre étrange, par exemple http://bugs.python.org/issue5099 . Doit être __del__appelé même si l' __init__échec? Voir http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 pour un long fil de discussion.

Mais d'autre part:

Et ma raison personnelle de ne pas aimer la __del__fonction.

  • Chaque fois que quelqu'un évoque __del__cela se transforme en trente messages de confusion.
  • Il casse ces éléments dans le Zen de Python:
    • Le simple vaut mieux que compliqué.
    • Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
    • Les erreurs ne devraient jamais passer silencieusement.
    • Face à l'ambiguïté, refusez la tentation de deviner.
    • Il devrait y avoir une - et de préférence une seule - façon évidente de le faire.
    • Si l'implémentation est difficile à expliquer, c'est une mauvaise idée.

Alors, trouvez une raison de ne pas utiliser __del__.

Charles Merriam
la source
6
Même si la question n'est pas exactement: pourquoi ne pas utiliser __del__, mais comment appeler __del__, votre réponse est intéressante.
nbro
Merci. Parfois, la meilleure idée est de s'éloigner des idées affreuses.
Charles Merriam
Dans d'autres nouvelles, j'ai oublié de mentionner que PyPy (un interpréteur plus rapide pour les applications plus longues) se cassera sur del .
Charles Merriam
Merci @Gloin d'avoir mis à jour le lien cassé!
Charles Merriam
@CharlesMerriam Merci vous pour la réponse!
Tom Burrows
13

La __del__méthode, elle sera appelée lorsque l'objet est garbage collection. Notez que l'appel n'est pas nécessairement garanti. Le code suivant ne le fera pas nécessairement:

del obj

La raison en est que deldécrémente simplement le nombre de références de un. Si quelque chose d'autre a une référence à l'objet, __del__il ne sera pas appelé.

Il y a cependant quelques mises en garde à utiliser __del__. En général, ils ne sont généralement pas très utiles. Il me semble que vous voulez utiliser une méthode close ou peut-être une instruction with .

Consultez la documentation python sur les __del__méthodes .

Une autre chose à noter: les __del__méthodes peuvent empêcher le garbage collection si elles sont surutilisées. En particulier, une référence circulaire qui a plus d'un objet avec une __del__méthode ne sera pas récupérée. C'est parce que le garbage collector ne sait pas lequel appeler en premier. Consultez la documentation sur le module gc pour plus d'informations.

Jason Baker
la source
8

La __del__méthode (notez l'orthographe!) Est appelée lorsque votre objet est finalement détruit. Techniquement parlant (en cPython) c'est quand il n'y a plus de références à votre objet, c'est à dire quand il sort de la portée.

Si vous souhaitez supprimer votre objet et appeler ainsi la __del__méthode utilisez

del obj1

qui supprimera l'objet (à condition qu'il n'y ait pas d'autres références à celui-ci).

Je vous suggère d'écrire un petit cours comme celui-ci

class T:
    def __del__(self):
        print "deleted"

Et étudiez dans l'interpréteur python, par exemple

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>   

Notez que jython et ironpython ont des règles différentes quant au moment exact où l'objet est supprimé et __del__appelé. __del__Cependant, il n'est pas considéré comme une bonne pratique à utiliser à cause de cela et du fait que l'objet et son environnement peuvent être dans un état inconnu lorsqu'il est appelé. Il n'est pas absolument garanti __del__qu'il sera appelé non plus - l'interpréteur peut quitter de différentes manières sans supprimer tous les objets.

Nick Craig-Wood
la source
1
comparé à stackoverflow.com/a/2452895/611007 et stackoverflow.com/a/1481512/611007 , use del obj1semble être une mauvaise idée sur laquelle s'appuyer.
n611x007
0

Comme mentionné précédemment, la __del__fonctionnalité n'est pas fiable. Dans les cas où cela peut sembler utile, envisagez d'utiliser les méthodes __enter__et à la __exit__place. Cela donnera un comportement similaire à la with open() as f: passsyntaxe utilisée pour accéder aux fichiers. __enter__est automatiquement appelé lors de l'entrée dans la portée de with, while __exit__est automatiquement appelé lors de sa sortie. Voir cette question pour plus de détails.

Somoria
la source