web-dev-qa-db-fra.com

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

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

83
Verrtex

__del__ Est un finaliseur. Il est appelé lorsqu'un objet est ramasse-miettes, 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 pourrait être juste après que vous ayez dit del x Ou, si x soit une variable locale, après la fin de la fonction. En particulier, à moins qu'il y ait des références circulaires, CPython (le standard Python)) sera immédiatement récupéré.

Cependant, il s’agit d’un détail d’implémentation de CPython. La seule obligatoire propriété de Python garbage collection est ce qui se passe après toutes les références ont été supprimé, donc cela pourrait ne pas nécessairement arriver juste après et pourrait ne pas arriver du tout.

Encore plus, les variables peuvent vivre longtemps beaucoup de raisons, par ex. une exception propagée ou une introspection de module peut conserver le nombre de références de variable supérieur à 0. De plus, variable peut faire partie de cycle de références - CPython avec le garbage collection activé active interrompt la plupart de ces cycles, mais pas tous. et même alors seulement périodiquement.

Puisque vous n'avez aucune garantie qu'il est exécuté, vous devriez jamais mettre le code que vous devez exécuter dans __del__() - à la place, ce code appartient à la clause finally de le bloc try ou à un gestionnaire de contexte dans une instruction with. Cependant, il existe cas d'utilisation valides pour __del__: Par ex. si un objet X fait référence à Y et conserve également une copie de Y référence dans un global cache (cache['X -> Y'] = Y), alors ce serait polite pour que X.__del__ supprime également l’entrée du cache.

Si vous savez que le destructeur fournit (en violation de la directive ci-dessus) un nettoyage requis, vous voudrez peut-être appelez-le directement, car il n'a rien de spécial en tant que méthode: x.__del__(). De toute évidence, 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  
135
ilya n.

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

Comment fonctionnent les constructeurs et les destructeurs?

Voici une réponse légèrement opinionée.

N'utilisez pas __del__. Ce n'est pas C++ ou un langage construit pour les destructeurs. La méthode __del__ Devrait vraiment être utilisée Python 3.x, bien que je sois sûr que quelqu'un trouvera un cas d'utilisation qui aura du sens. Si vous avez besoin d'utiliser __del__, Soyez conscient des limitations de base de http://docs.python.org/reference/datamodel.html :

  • __del__ Est appelé lorsque le ramasse-miettes récupère les objets, pas lorsque vous perdez la dernière référence à un objet ni lorsque vous exécutez del object.
  • __del__ Est responsable d'appeler n'importe quel __del__ Dans une superclasse, bien qu'il ne soit pas clair s'il s'agit d'un ordre de résolution de méthode (MRO) ou s'il appelle simplement chaque superclasse.
  • Avoir un __del__ Signifie que le ramasse-miettes abandonne la détection et le nettoyage des liens cycliques, tels que 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 le cycle complètement. Cela fait l'objet d'un débat de temps en temps: voir http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • La fonction __del__ Peut tricher en enregistrant une référence à un objet et en arrêtant le garbage collection.
  • Les exceptions explicitement soulevé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é-de - 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 ordures sont collectées avant de quitter, et il y a beaucoup de problèmes étranges. L'appel de __del__ Sur globals provoque des problèmes d'ordre inhabituels, par exemple, http://bugs.python.org/issue5099 . Est-ce que __del__ Doit être appelé même si le __init__ Échoue? Voir http://mail.python.org/pipermail/python-dev/2000-March/thread.html#242 pour un long fil.

Mais d'autre part:

Et ma raison principale de ne pas aimer la fonction __del__.

  • Chaque fois que quelqu'un appelle __del__, Il se décompose en trente messages de confusion.
  • Il casse ces objets dans le zen de Python:
    • Simple c'est mieux que compliqué.
    • Les cas spéciaux ne sont pas assez spéciaux pour enfreindre les règles.
    • Les erreurs ne doivent jamais passer silencieusement.
    • Face à l'ambiguïté, refusez la tentation de deviner.
    • Il devrait y avoir un - et de préférence un seul - moyen évident de le faire.
    • Si la mise en œuvre est difficile à expliquer, c'est une mauvaise idée.

Alors, trouvez une raison de ne pas utiliser __del__.

62
Charles Merriam

Le __del__ _ méthode, elle sera appelée lorsque l’objet est nettoyé. Notez qu'il n'est pas nécessairement garanti d'être appelé cependant. Le code suivant par lui-même ne le fera pas nécessairement:

del obj

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

Il y a quelques mises en garde concernant l'utilisation de __del__ bien que. Généralement, ils ne sont généralement pas très utiles. Il me semble que vous voulez utiliser une méthode proche ou peut-être un avec instruction .

Voir la documentation python sur __del__ méthodes .

Une autre chose à noter: __del__ méthodes peuvent empêcher le ramassage des ordures en cas de surutilisation. En particulier, une référence circulaire comportant plusieurs objets avec un __del__ La méthode ne récupère pas les ordures. En effet, le ramasse-miettes ne sait pas lequel appeler en premier. Voir la documentation sur le module gc pour plus d'informations.

12
Jason Baker

Le __del__ méthode (note orthographe!) est appelée lorsque votre objet est finalement détruit. Techniquement parlant (en cPython), c’est-à-dire quand il n’ya 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 le __del__ utilisation de la méthode

del obj1

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

Je vous suggère d'écrire une petite classe comme celle-ci

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

Et recherchez dans l’interprète 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 à la date exacte de suppression de l'objet et à __del__ est appelé. Ce n'est pas considéré comme une bonne pratique d'utiliser __del__ cependant à cause de cela et du fait que l'objet et son environnement peuvent être dans un état inconnu lors de son appel. Ce n'est pas absolument garanti __del__ sera appelé soit - l’interprète peut sortir de différentes manières sans supprimer tous les objets.

8
Nick Craig-Wood

Comme mentionné précédemment, la fonctionnalité __del__ Est quelque peu peu fiable. Si cela vous semble utile, envisagez plutôt d'utiliser les méthodes __enter__ Et __exit__. Cela donnera un comportement similaire à la syntaxe with open() as f: pass utilisée pour accéder aux fichiers. __enter__ Est automatiquement appelé lors de la saisie de la portée de with, tandis que __exit__ Est automatiquement appelé lors de sa sortie. Voir cette question pour plus de détails.

0
somoria