2011-04-18 11 views
8

Consideremos el siguiente código de ejemploEn la segunda inicialización de un objeto, ¿por qué __init__ se llama antes de __del__?

class A: 
    def __init__(self, i): 
     self.i = i 
     print("Initializing object {}".format(self.i)) 

    def __del__(self): 
     print("Deleting object {}".format(self.i)) 

for i in [1, 2]: 
    a = A(i) 

Creación del objeto dentro del bucle se pretende asegurar que el destructor de A sería llamado antes se crearía el nuevo Un objeto. Pero al parecer, ocurre lo siguiente:

objeto Inicialización 1

objeto Inicialización 2

objeto Eliminación de 1

objeto Eliminación de 2

¿Por qué es el destructor del objeto 1 solamente llamado después de que el nuevo objeto se ha inicializado? ¿Es esto un comportamiento intencional? Sé que el bucle for no tiene un alcance propio en python. En C++, por ejemplo, el destructor de 1 ciertamente se llamaría antes del constructor para el objeto 2 (al menos si el objeto se declara dentro del bucle).

En mi programa, quiero asegurarme de que el objeto viejo se elimine antes de que se cree el nuevo. ¿Hay alguna otra posibilidad aparte de eliminar a explícitamente al final del ciclo for?

Gracias de antemano.

+0

Suceso interesante si cambia el bucle a más '[1,2,3]'. – kennytm

+1

Esto sería una buena pregunta para la entrevista. – pajton

+4

Re último párrafo: no desea hacer eso. Porque no se puede (GC no es determinista y el recuento es un detalle de implementación), y no es necesario hacerlo de todos modos. Si tiene recursos para eliminar, use un administrador de contexto (instrucción 'with') o agregue un método como' .close() 'y asegúrese de que se invoque. – delnan

Respuesta

11

La creación del segundo objeto ocurre antes de que el nombre se recupere y el primer objeto se elimine.

  1. Se crea la primera instancia de A.
  2. a está obligado.
  3. Se crea una instancia de la segunda A.
  4. a es rebote, y el primer A se desecha.
  5. El programa finaliza y el segundo A se desecha.
+0

¡Gracias por la respuesta rápida y detallada! ¿Debo usar 'del' para estar seguro, la instancia anterior se elimina entonces? –

+0

no es este detalle de implementación? –

+0

Tienes razón. Déjame ponerlo de esta manera: ¿Supongo que no hay forma de implementar esto dentro de la definición de la clase A? –

9

No puede confiar en los detalles de implementación del recolector de elementos no utilizados cuando planifica dependencias de por vida. Necesitas hacer esto explícitamente de una forma u otra.

Administradores de contexto vienen a la mente, por ejemplo:

from contextlib import contextmanager 

@contextmanager 
def deleting(obj): 
    try: 
     yield 
    finally: 
     del(obj) 

class A: 
    def __init__(self, i): 
     self.i = i 
     print("Initializing object {}".format(self.i)) 

    def __del__(self): 
     print("Deleting object {}".format(self.i)) 

for i in [1,2]: 
    with deleting(A(i)) as obj: 
     pass 

print 

for i in [1,2]: 
    a = A(i) 

Esto produce el siguiente resultado:

Initializing object 1 
Deleting object 1 
Initializing object 2 
Deleting object 2 

Initializing object 1 
Initializing object 2 
Deleting object 1 
Deleting object 2 
+0

Muchas gracias por la respuesta detallada –

0

Suponiendo que desea que el objeto que se define como su valor final, cuando el bucle termina , no elimine el objeto explícitamente en el extremo del bucle for, hágalo en el comenzando del bucle, como este:

class A: 
    def __init__(self, i): 
     self.i = i 
     print("Initializing object {}".format(self.i)) 

    def __del__(self): 
     print("Deleting object {}".format(self.i)) 

for i in [1, 2, 3]: 
    a = None 
    a = A(i) 

que imprime:

Initializing object 1 
Deleting object 1 
Initializing object 2 
Deleting object 2 
Initializing object 3 
Deleting object 3 

(Nota: Ignatio tiene razón sobre por qué funciona la forma en que funciona, pero KennyTM es correcto, también, que para hacer más evidente lo que está sucediendo se debe hacer ir al menos tres veces a través del bucle.)

Cuestiones relacionadas