Pregunta

Soy nuevo en Python y he estado trabajando a través de los ejemplos en Swaroop CH de "un byte de Python". Estoy viendo un comportamiento con el método __del__ que me es desconcertante.

Básicamente, si funciono el siguiente script (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')

utilizando la consola de Python (o la consola interactiva Spyder) Veo lo siguiente:

  
    
      

execfile (u'C: \ 1_eric \ Python \ test1.py ')
      La inicialización de Swaroop
      Inicializando Kalem

             

execfile (u'C: \ 1_eric \ Python \ test1.py ')
      La inicialización de Swaroop
      Swaroop dice adiós
      Soy el último
      La inicialización de Kalem
      Kalem dice adiós
      Soy el último

    
  

¿Por qué es el método __del__ ser llamado inmediatamente después de la __init__ en la segunda pasada?
Supongo que ya que los mismos nombres de instancia ( 'Swaroop' y 'Kaleem') están siendo utilizados que es la liberación de la instancia original y la basura recogiéndola. Sin embargo, esto parece estar haciendo estragos en el recuento de la población actual.

¿Qué está pasando aquí?
¿Qué es una buena manera de evitar este tipo de confusión?
   Evitar el uso de __del__?    Comprobar si los nombres de instancia existente antes de volver a utilizarlos?    ...

Gracias, Eric

¿Fue útil?

Solución

Indicaciones generales: no utilice __ __ del en Python. Se puede romper la recolección de basura en un número de maneras, esp. en el caso de referencias circulares entre los objetos.

En el ejemplo, hay diversas cuestiones relacionadas con el uso de execfile () - que no es una buena práctica - y la redefinición de las variables globales. Por cierto, si realmente se necesita para crear un pseudo-destructor (es decir, un código que se invoca cada vez que el objeto se recoge la basura), escribir una función llamada "finalizador" (que no es propiamente un destructor) e invocar usando weakref devolución de llamada .ref. No debería ser un método de instancia, por supuesto, y recordar que lambda en realidad crea un cierre, por lo tanto, asegúrese de que no se escape ninguna referencia al mismo en la devolución de llamada! Si necesita los datos de la instancia destruidos, utilizar el enfoque argumento predeterminado func, sólo asegúrese no para hacer referencia a 'yo' dentro de la lambda, de lo contrario no funcionará.

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)

salida (el sueño está ahí para ayudar a ver lo que está pasando):

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

Otros consejos

Hay un par de cosas que hacer aquí. Cuando se instancia la clase Person4, se inicializa la variable de clase population a 0. A partir de su consola interactiva, Parece que está ejecutando sus archivos "test1.py" varias veces. La segunda vez que se ejecuta, la clase Person4 se declara de nuevo que hace que sea técnicamente diferente en la primera (a pesar de que tiene el mismo nombre). Eso significa que tiene su propio recuento population independiente.

Ahora, swaroop y kaleem son Global variables compartidas entre ambas instancias de "test1.py". Python usa internamente el recuento de referencias para la mayor parte de su recolección de basura automática, por lo que la instancia original de la primera clase Person4 no se libera hasta la segunda asignación a swaroop. Asignando a swaroop decrementa el contador de referencia para la primera instancia, causando __del__ ser llamado debido a que la cuenta de referencia es ahora cero. Pero debido a que se está refiriendo a Person4 por su nombre en el interior __del__(), cuando el anterior ejemplo desaparece se disminuye el nueva Person4.population recuento, en lugar de la antigua recuento de la población Person4.

Con suerte que tenía sentido. Puedo ver por qué esto puede ser confuso para alguien aprender Python. Su uso de las variables de clase, al mismo tiempo que la redefinición de la clase Person4 usando execfile() es más asuntos confusos. Por lo que vale la pena, he escrito mucho código Python y yo no creo que nunca he necesitado utilizar el método especial __del__.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top