class Package:
def __init__(self):
self.files = []
# ...
def __del__(self):
for file in self.files:
os.unlink(file)
__del__(self)
arriba falla con una excepción AttributeError. Entiendo que Python no garantiza la existencia de "variables globales" (¿datos de miembros en este contexto?) Cuando __del__()
se invoca. Si ese es el caso y esta es la razón de la excepción, ¿cómo me aseguro de que el objeto se destruya correctamente?
python
destructor
Wilhelmtell
fuente
fuente
__del__
qué no debe usarse como contraparte de__init__
. (Es decir, no es un "destructor" en el sentido de que__init__
es un constructor.Respuestas:
Recomiendo usar la
with
declaración de Python para administrar los recursos que necesitan ser limpiados. El problema con el uso de unaclose()
declaración explícita es que debe preocuparse de que las personas se olviden de llamarlo o se olviden de colocarlo en unfinally
bloque para evitar una pérdida de recursos cuando se produce una excepción.Para usar la
with
instrucción, cree una clase con los siguientes métodos:En su ejemplo anterior, usaría
Luego, cuando alguien quisiera usar tu clase, haría lo siguiente:
La variable package_obj será una instancia de tipo Package (es el valor devuelto por el
__enter__
método). Se__exit__
llamará automáticamente a su método, independientemente de si se produce o no una excepción.Incluso podría llevar este enfoque un paso más allá. En el ejemplo anterior, alguien aún podría crear una instancia de Package usando su constructor sin usar la
with
cláusula. No quieres que eso suceda. Puede solucionar esto creando una clase PackageResource que defina los métodos__enter__
y__exit__
. Luego, la clase Package se definiría estrictamente dentro del__enter__
método y se devolvería. De esa manera, la persona que llama nunca podría crear una instancia de la clase Package sin usar unawith
declaración:Usarías esto de la siguiente manera:
fuente
with Resource(param1, param2) as r: # ...
La forma estándar es usar
atexit.register
:Pero debe tener en cuenta que esto persistirá en todas las instancias creadas
Package
hasta que Python finalice.Demostración utilizando el código anterior guardado como package.py :
fuente
with
? ¿Llamaron explícitamente__enter__
?) La desventaja es, por supuesto, si necesita que se realice la limpieza antes de Python salidas, no funcionará. En mi caso, no me importa si es cuando el objeto está fuera de alcance o si no es hasta que Python salga. :)atexit.register(self.__exit__)
?__exit__
y usar un administrador de contexto? Además,__exit__
toma argumentos adicionales (es decir__exit__(self, type, value, traceback)
), por lo que necesitaría buscarlos. De cualquier manera, parece que debería publicar una pregunta por separado en SO, porque su caso de uso parece inusual.Como apéndice a la respuesta de Clint , puede simplificar el
PackageResource
uso decontextlib.contextmanager
:Alternativamente, aunque probablemente no sea Pythonic, puede anular
Package.__new__
:y simplemente usar
with Package(...) as package
.Para acortar las cosas, asigne un nombre a su función de limpieza
close
y úselacontextlib.closing
, en cuyo caso puede usar laPackage
clase no modificadawith contextlib.closing(Package(...))
o anularla__new__
a la más simple.Y este constructor se hereda, por lo que simplemente puede heredar, por ejemplo
fuente
Package.__new__()
embargo, es lamentable que no podamos evitar las repeticiones de cuatro líneas del método. O tal vez podamos. Probablemente podríamos definir un decorador de clases o una metaclase que genérico para nosotros. Alimento para el pensamiento pitónico.Package
también debería hacer esto (aunque todavía no lo he probado), por lo que no se requiere una metaclase. A pesar de que he encontrado algunas formas muy curiosas para utilizar metaclases en el pasado ...Package
(o mejor una clase llamadaClosing
) como su padre de clase en lugar deobject
. Pero no me pregunten cómo la herencia múltiple seNo creo que sea posible eliminar miembros antes de que
__del__
se llame. Supongo que la razón de su AttributeError particular está en otro lugar (tal vez elimine por error el archivo self en otro lugar).Sin embargo, como señalaron los demás, debe evitar su uso
__del__
. La razón principal de esto es que las instancias con__del__
no se recolectarán basura (solo se liberarán cuando su recuento llegue a 0). Por lo tanto, si sus instancias están involucradas en referencias circulares, vivirán en la memoria mientras se ejecute la aplicación. (Sin embargo, puedo estar equivocado acerca de todo esto, tendría que leer los documentos de gc nuevamente, pero estoy bastante seguro de que funciona así).fuente
__del__
pueden recolectarse basura si su recuento de referencia de otros objetos con__del__
es cero y no se puede alcanzarlos. Esto significa que si tiene un ciclo de referencia entre objetos con__del__
, ninguno de esos será recolectado. Cualquier otro caso, sin embargo, debe resolverse como se esperaba.Una mejor alternativa es usar weakref.finalize . Vea los ejemplos en Objetos finalizadores y Comparación de finalizadores con los métodos __del __ () .
fuente
stop()
método para cerrar los puertos yjoin()
los procesos. Sin embargo, si el programa se cierra inesperadamente,stop()
no se llama, lo resolví con un finalizador. Pero en cualquier caso, llamo_finalizer.detach()
al método de detención para evitar llamarlo dos veces (manualmente y luego nuevamente por el finalizador).Creo que el problema podría estar
__init__
si hay más código del que se muestra.__del__
se llamará incluso cuando__init__
no se haya ejecutado correctamente o haya lanzado una excepción.Fuente
fuente
__del__
es declarar explícitamente a todos los miembros a nivel de clase, asegurando que siempre existan, incluso si__init__
falla. En el ejemplo dado,files = ()
funcionaría, aunque en su mayoría solo se asignaríaNone
; en cualquier caso, aún necesita asignar el valor real en__init__
.Aquí hay un esqueleto de trabajo mínimo:
Importante: volver a sí mismo
Si eres como yo y pasas por alto la
return self
parte (de la respuesta correcta de Clint Miller ), estarás mirando estas tonterías:Espero que ayude a la próxima persona.
fuente
Simplemente envuelva su destructor con una declaración try / except y no arrojará una excepción si sus globals ya están eliminados.
Editar
Prueba esto:
Rellenará la lista de archivos en la función del que se garantiza que existe en el momento de la llamada. El proxy weakref es evitar que Python, o usted mismo, elimine la variable self.files de alguna manera (si se elimina, no afectará la lista de archivos original). Si no es el caso de que esto se esté eliminando aunque haya más referencias a la variable, puede eliminar la encapsulación del proxy.
fuente
Parece que la forma idiomática de hacer esto es proporcionar un
close()
método (o similar) y llamarlo explícitamente.fuente