class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
ci-dessus échoue avec une exception AttributeError. Je comprends que Python ne garantit pas l'existence de "variables globales" (données de membre dans ce contexte?) Lorsqu'il __del__()
est invoqué. Si tel est le cas et que c'est la raison de l'exception, comment puis-je m'assurer que l'objet se détruit correctement?
python
destructor
wilhelmtell
la source
la source
__del__
ne pas l'utiliser comme contrepartie__init__
. (C'est-à-dire que ce n'est pas un "destructeur" au sens où__init__
c'est un constructeur.Réponses:
Je recommande d'utiliser la
with
déclaration de Python pour gérer les ressources qui doivent être nettoyées. Le problème avec l'utilisation d'uneclose()
déclaration explicite est que vous devez vous inquiéter du fait que les gens oublient de l'appeler ou oublient de le placer dans unfinally
bloc pour éviter une fuite de ressources lorsqu'une exception se produit.Pour utiliser l'
with
instruction, créez une classe avec les méthodes suivantes:Dans votre exemple ci-dessus, vous utiliseriez
Ensuite, lorsque quelqu'un voulait utiliser votre classe, il procédait comme suit:
La variable package_obj sera une instance de type Package (c'est la valeur retournée par la
__enter__
méthode). Sa__exit__
méthode sera automatiquement appelée, qu'une exception se produise ou non.Vous pourriez même aller plus loin dans cette approche. Dans l'exemple ci-dessus, quelqu'un pourrait toujours instancier Package en utilisant son constructeur sans utiliser la
with
clause. Vous ne voulez pas que cela se produise. Vous pouvez résoudre ce problème en créant une classe PackageResource qui définit les méthodes__enter__
et__exit__
. Ensuite, la classe Package serait définie strictement à l'intérieur de la__enter__
méthode et renvoyée. De cette façon, l'appelant n'a jamais pu instancier la classe Package sans utiliser unewith
instruction:Vous utiliseriez ceci comme suit:
la source
with Resource(param1, param2) as r: # ...
La méthode standard consiste à utiliser
atexit.register
:Mais vous devez garder à l'esprit que toutes les instances créées persisteront
Package
jusqu'à ce que Python soit terminé.Démo utilisant le code ci-dessus enregistré sous package.py :
la source
with
? Ont-ils appelé explicitement__enter__
?) L'inconvénient est bien sûr si vous avez besoin que le nettoyage se produise avant python sort, ça ne marchera pas. Dans mon cas, je me fiche que ce soit lorsque l'objet sort de la portée ou si ce n'est pas jusqu'à la sortie de python. :)atexit.register(self.__exit__)
?__exit__
et utiliser un gestionnaire de contexte? Prend également__exit__
des arguments supplémentaires (c.-à-d.__exit__(self, type, value, traceback)
), Vous devrez donc vous en procurer. Quoi qu'il en soit, il semble que vous devriez publier une question distincte sur SO, car votre cas d'utilisation semble inhabituel?En annexe à la réponse de Clint , vous pouvez simplifier en
PackageResource
utilisantcontextlib.contextmanager
:Alternativement, mais probablement pas en Pythonic, vous pouvez remplacer
Package.__new__
:et utilisez simplement
with Package(...) as package
.Pour raccourcir les choses, nommez
close
et utilisez votre fonction de nettoyagecontextlib.closing
, auquel cas vous pouvez soit utiliser laPackage
classe non modifiée via,with contextlib.closing(Package(...))
soit la remplacer__new__
par la plus simple.Et ce constructeur est hérité, vous pouvez donc simplement hériter, par exemple
la source
Package.__new__()
méthode. Ou peut-être que nous pouvons. Nous pourrions probablement définir soit un décorateur de classe, soit une métaclasse généralisant ce passe-partout pour nous. Aliments pour la pensée pythonique.Package
devrait également le faire (même si je ne l'ai pas encore testé), donc aucune métaclasse ne devrait être requise. Bien que j'ai trouvé des moyens assez curieux d'utiliser métaclasses dans le passé ...Package
(ou mieux une classe nomméeClosing
) comme parent de classe au lieu deobject
. Mais ne me demandez pas comment l'héritage multiple gâche cela ...Je ne pense pas qu'il soit possible par exemple de supprimer des membres avant d'
__del__
appeler. Je suppose que la raison de votre AttributeError particulière est ailleurs (peut-être que vous supprimez par erreur self.file ailleurs).Cependant, comme les autres l'ont souligné, vous devez éviter de l'utiliser
__del__
. La raison principale en est que les instances avec__del__
ne seront pas récupérées (elles ne seront libérées que lorsque leur refcount atteindra 0). Par conséquent, si vos instances sont impliquées dans des références circulaires, elles resteront en mémoire aussi longtemps que l'application s'exécutera. (Je me trompe peut-être cependant, je devrais relire les documents gc, mais je suis plutôt sûr que cela fonctionne comme ça).la source
__del__
peuvent être récupérés si leur nombre de références à partir d'autres objets avec__del__
est nul et s'ils ne sont pas accessibles. Cela signifie que si vous avez un cycle de référence entre les objets avec__del__
, aucun de ceux-ci ne sera collecté. Cependant, tout autre cas doit être résolu comme prévu.Une meilleure alternative consiste à utiliser le fichier lowref.finalize . Voir les exemples dans Finalizer Objects et Comparaison des finaliseurs avec les méthodes __del __ () .
la source
stop()
méthode pour fermer les ports etjoin()
les processus. Cependant, si le programme se ferme de façon inattendue, ilstop()
n'est pas appelé - j'ai résolu cela avec un finaliseur. Mais dans tous les cas, j'appelle_finalizer.detach()
la méthode stop pour éviter de l'appeler deux fois (manuellement et plus tard par le finaliseur).Je pense que le problème pourrait être
__init__
s'il y a plus de code que celui indiqué?__del__
sera appelé même s'il__init__
n'a pas été exécuté correctement ou a levé une exception.La source
la source
__del__
est de déclarer explicitement tous les membres au niveau de la classe, en s'assurant qu'ils existent toujours, même en cas d'__init__
échec. Dans l'exemple donné,files = ()
cela fonctionnerait, bien que la plupart du temps, vous ne fassiez qu'assignerNone
; dans les deux cas, vous devez toujours affecter la valeur réelle à__init__
.Voici un squelette de travail minimal:
Important: retournez-vous
Si vous êtes comme moi et que vous négligez la
return self
partie (de la bonne réponse de Clint Miller ), vous regarderez ce non-sens:J'espère que cela aide la prochaine personne.
la source
Enveloppez simplement votre destructeur avec une instruction try / except et il ne lèvera pas d'exception si vos globaux sont déjà éliminés.
Éditer
Essaye ça:
Il remplira la liste des fichiers dans la fonction del qui est garantie d'exister au moment de l'appel. Le proxy de faiblesse est d'empêcher Python ou vous-même de supprimer la variable self.files d'une manière ou d'une autre (si elle est supprimée, cela n'affectera pas la liste de fichiers d'origine). Si ce n'est pas le cas que cela est supprimé même s'il y a plus de références à la variable, vous pouvez supprimer l'encapsulation du proxy.
la source
Il semble que la façon idiomatique de le faire est de fournir une
close()
méthode (ou similaire), et de l'appeler explicitement.la source