méthode __del__ appelé en python quand il ne devrait
-
20-09-2019 - |
Question
Je suis nouveau python et travaille à travers les exemples dans « un octet de Python » Swaroop CH de. Je vois un comportement avec la méthode __del__
qui me laisse perplexe.
En fait, si je lance le script suivant (en Python 2.6.2)
class Person4:
'''Represents a person'''
population = 0
def __init__(self, name):
'''Initialize the person's data'''
self.name = name
print 'Initializing %s'% self.name
#When the person is created they increase the population
Person4.population += 1
def __del__(self):
'''I am dying'''
print '%s says bye' % self.name
Person4.population -= 1
if Person4.population == 0:
print 'I am the last one'
else:
print 'There are still %d left' % Person4.population
swaroop = Person4('Swaroop')
kaleem = Person4('Kalem')
l'aide de la console de python (ou la console interactive Spyder) Je vois ce qui suit:
execfile (u'C: \ 1_eric \ Python \ test1.py ')
Swaroop
Initialisation Kalem Initializingexecfile (u'C: \ 1_eric \ Python \ test1.py ')
Swaroop
Initialisation Swaroop dit au revoir
Je suis le dernier
Kalem
Initialisation Kalem dit au revoir
Je suis le dernier
Pourquoi la méthode __del__
étant appelée immédiatement après la __init__
la deuxième manche?
Je suppose que, puisque les mêmes noms d'instance ( « de Swaroop » et « Kaleem ») sont utilisés que publie l'instance d'origine et la collecte des ordures il. Mais, cela semble jouer des ravages avec le nombre actuel de la population.
Qu'est-ce qui se passe ici?
Qu'est-ce qu'un bon moyen d'éviter ce genre de confusion?
Évitez l'utilisation de __del__
?
Vérifiez les noms d'instance existants avant de les réutiliser?
...
Merci, Eric
La solution
Conseils généraux: ne pas utiliser __ __ del en Python. Il peut briser la collecte des ordures dans un certain nombre de façons, esp. dans le cas des références circulaires entre les objets.
Dans votre exemple, il sont diverses questions liées à l'utilisation de execfile () - qui n'est pas une meilleure pratique - et la redéfinition des variables globales. Soit dit en passant, si vous avez vraiment besoin de créer un pseudo-destructor (par exemple un code qui est invoqué chaque fois que l'objet se ramasse-miettes), écrire une soi-disant fonction « finaliseur » (ce n'est pas correctement une destructor) et appeler à l'aide weakref rappel .ref. Il ne devrait pas être une méthode d'instance bien sûr, et rappelez-vous que lambda crée en fait une fermeture, être donc sûr de ne pas fuir toute référence à l'auto dans le rappel! Si vous avez besoin des données de l'instance détruite, utilisez l'approche de l'argument par défaut Func, juste être sûr jamais pour faire référence à « moi » à l'intérieur du lambda, sinon cela ne fonctionnera pas.
from weakref import ref
from time import sleep
class Person4:
'''Represents a person'''
population = 0
def __init__(self, name):
'''Initialize the person's data'''
self.name = name
print 'Initializing %s'% self.name
#When the person is created they increase the population
Person4.population += 1
self._wr = ref(self, lambda wr, name=self.name: Person4_finalizer(name))
def Person4_finalizer(name):
'''I am dying'''
print '%s says bye' % name
Person4.population -= 1
if Person4.population == 0:
print 'I am the last one'
else:
print 'There are still %d left' % Person4.population
p1 = Person4("one")
p2 = Person4("two")
p3 = Person4("three")
del p2
del p3
sleep(5)
sortie (le sommeil est là pour vous aider à voir ce qui se passe):
Initializing one
Initializing two
Initializing three
two says bye
There are still 2 left
three says bye
There are still 1 left
one says bye
I am the last one
Autres conseils
Il y a deux choses qui se passent ici. Lorsque votre classe Person4
est instancié, il initialise sa variable de classe population
à 0. À partir de votre console interactive, vous semblez être en cours d'exécution le fichier « test1.py » plusieurs fois. La deuxième fois que vous l'exécutez, la classe Person4
est déclarée à nouveau ce qui le rend techniquement différents de la première (même si elle a le même nom). Cela signifie qu'il a son propre compte de population
indépendant.
Maintenant, swaroop
et kaleem
sont global variables partagées entre les deux instances du "test1.py". Python utilise en interne comptage de référence pour la plupart de sa collecte des ordures automatique, de sorte que l'instance d'origine de la première classe Person4
n'est pas libéré jusqu'à ce que la seconde affectation à swaroop
. Attribution de swaroop
décrémente le compte de référence pour la première instance, ce qui __del__
être appelé parce que le compte de référence est maintenant zéro. Mais parce que vous faites référence à Person4
par nom à l'intérieur __del__()
, lorsque le précédent par exemple disparait décrémente nouveau nombre Person4.population
, au lieu de l'ancien comte de population Person4
.
Il faut espérer que du sens. Je ne vois pas pourquoi cela pourrait être source de confusion pour quelqu'un d'apprendre Python. Votre utilisation de variables de classe en même temps que la redéfinition de la classe Person4
à l'aide execfile()
est d'autres questions déroutantes. Pour ce que ça vaut, j'ai écrit beaucoup de code Python et je ne pense pas que j'ai jamais eu besoin d'utiliser la méthode spéciale __del__
.