2012-01-18 9 views
6

Tengo una clase que abre un archivo para escribir. En mi destructor, llamo a la función que cierra el archivo:del MyClass no llama al objeto .__ del __()

class MyClass: 
    def __del__(self): 
     self.close() 

    def close(self): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

pero cuando se borra el objeto con un código como:

myobj = MyClass() 
myobj.open() 
del myobj 

si trato de reinstantiate el objeto, me sale un error de valor :

ValueError: The file 'filename' is already opened. Please close it before reopening in write mode. 

mientras que si llamo myobj.close() antes del myobj no entiendo este problema. Entonces, ¿por qué no se llama a __del__()?

+1

Intente heredar de 'object' - no hacerlo se ha considerado un estilo desactualizado durante unos seis años. – Marcin

+0

Deberías mostrar más código. ¿Cómo se puede decir cómo 'myobj.close()' cambia las cosas, sin saber lo que hace? – ugoren

+1

Tenga en cuenta que 'del' no llama a' __del__'. Simplemente elimina una de las referencias. En general, es mejor cerrar explícitamente, posiblemente utilizando el protocolo del gestor de contexto (instrucción 'with'). – yak

Respuesta

8

¿Estás seguro de que deseas utilizar __del__? Hay issues con __del__ y recolección de basura.

Se podría hacer MiClase un context manager lugar:

class MyClass(object): 
    def __enter__(self): 
     return self 
    def __exit__(self,ext_type,exc_value,traceback): 
     if self.__fileHandle__ is not None: 
       self.__fileHandle__.close() 

De esta manera, se puede usar MyClass así:

with MyClass() as myobj: 
    ... 

y myobj.__exit__ (y por lo tanto self.__fileHandle__.close()) será llamada cuando las hojas de Python el with-block.

+0

+1 por mencionar los problemas. http://docs.python.org/library/gc.html#gc.garbage dice que «los objetos que tienen métodos __del __() y son parte de un ciclo de referencia causan que todo el ciclo de referencia sea incobrable, incluidos los objetos que no necesariamente están en el ciclo, pero accesible solo desde allí. » –

+0

No veo cómo hay un ciclo de referencia en el ejemplo del PO. Hay un objeto, obtiene 'del''d, y' __del __() 'no se llama. – sudo

+1

Además, en mi situación, prefiero dejar que el GC maneje las cosas, ya que no es necesario que todas se cierren de inmediato, solo eventualmente ... pero siempre tiene problemas. Y no, los administradores de contexto no son una solución real. Le restringen a cerrar el objeto dentro de la misma llamada a función e introducir un código en el código un nivel cada vez que usa uno. Lo hace ilegible cuando quiero crear 20 de estos. – sudo

8

Eso no es lo que del hace. Es desafortunado que __del__ tenga el mismo nombre que del, porque no están relacionados entre sí. En terminología moderna, el método __del__ se llamaría finalizador, no destructor y la diferencia es importante.

La corta diferencia es que es fácil de garantizar un destructor cuando se llama, pero hay muy pocas garantías sobre cuándo __del__ serán llamados y podría no ser llamado. Hay muchas circunstancias diferentes que pueden causar esto.

Si desea un alcance léxico, use una declaración with. De lo contrario, llame directamente al myobj.close(). La instrucción del solo elimina referencias, no objetos.

Encontré otra respuesta (link) a una pregunta diferente que responde a esto con más detalle. Es desafortunado que la respuesta aceptada a esa pregunta contenga errores atroces.

Editar: Como notaron los comentaristas, debe heredar de object. Eso está bien, pero aún es posible que __del__ nunca sea llamado (podría tener suerte). Vea la respuesta vinculada arriba.

2

Su código debe heredar de object - no hacerlo se ha considerado obsoleto (excepto en casos especiales) durante al menos seis años.

se puede leer acerca __del__ aquí: http://docs.python.org/reference/datamodel.html#object.del

La versión corta de por qué tiene que heredar de object es que __del__ sólo es "mágica" en las clases de nuevo estilo.

Si necesita confiar en las llamadas de un finalizador, le sugiero encarecidamente que utilice el enfoque de gestor de contexto recomendado en otras respuestas, porque esa es una solución portátil y sólida.

+1

Ah acabo de encontrar una instancia donde esta "magia" no funciona ... utilizando ipython en su lugar. – tdc

+0

@tdc: A menos que esté equivocado, ipython se basa en cpython. En cualquier caso, si confía en el comportamiento de una instancia particular, su código no será portátil. Probablemente sería mejor utilizar el enfoque de gestor de contexto recomendado en otras respuestas. – Marcin

0

Quizás algo más se está refiriendo a él, razón por la cual __del__ no se está llamando (todavía).

consideran este código:

#!/usr/bin/env python 

import time 

class NiceClass(): 
    def __init__(self, num): 
     print "Make %i" % num 
     self.num = num 
    def __del__(self): 
     print "Unmake %i" % self.num 

x = NiceClass(1) 
y = NiceClass(2) 
z = NiceClass(3) 
lst = [x, y, z] 
time.sleep(1) 
del x 
del y 
del z 
time.sleep(1) 
print "Deleting the list." 
del lst 
time.sleep(1) 

No llama __del__ de NiceClass casos, hasta que eliminemos la lista que hace referencia a ellos.

A diferencia de C++, __del__ no se llama incondicionalmente para destruir el objeto bajo demanda. GC hace las cosas un poco más difíciles. Aquí hay alguna información: http://arctrix.com/nas/python/gc/

+0

Definitivamente hubo solo una referencia en este caso – tdc

Cuestiones relacionadas