¿Qué es el método __del__, cómo llamarlo?

108

Estoy leyendo un código. Hay una clase en la que __del__se define el método. Descubrí que este método se usa para destruir una instancia de la clase. Sin embargo, no puedo encontrar un lugar donde se utilice este método. La razón principal de esto es que no sé cómo se usa este método, probablemente no es así: obj1.del(). Entonces, mi pregunta es ¿cómo llamar al __del__método?

Verrtex
fuente

Respuestas:

168

__del__es un finalizador . Se llama cuando un objeto se recolecta como basura, lo que ocurre en algún momento después de que se hayan eliminado todas las referencias al objeto.

En un caso simple, esto podría ser justo después de decir del xo, si xes una variable local, después de que finalice la función. En particular, a menos que haya referencias circulares, CPython (la implementación estándar de Python) recolectará basura inmediatamente.

Sin embargo, este es un detalle de implementación de CPython. La única propiedad requerida de la recolección de basura de Python es que ocurre después de que se han eliminado todas las referencias, por lo que es posible que esto no suceda inmediatamente después y que no suceda en absoluto .

Aún más, las variables pueden vivir durante mucho tiempo por muchas razones , por ejemplo, una excepción que se propaga o la introspección del módulo pueden mantener el recuento de referencias de variables mayor que 0. Además, la variable puede ser parte del ciclo de referencias : CPython con la recolección de basura activada se interrumpe más , pero no todos, esos ciclos, e incluso entonces solo periódicamente.

Dado que no tiene garantía de que se ejecute, nunca se debe poner el código con el que debe ejecutarse; en __del__()cambio, este código pertenece a la finallycláusula del trybloque oa un administrador de contexto en una withdeclaración. Sin embargo, hay casos de uso válidos para __del__: por ejemplo, si un objeto hace Xreferencia Yy también mantiene una copia de la Yreferencia en un global cache( cache['X -> Y'] = Y), sería de buena educación X.__del__eliminar también la entrada de la caché.

Si usted sabe que el destructor proporciona (en violación de la directriz anterior) una limpieza necesaria, es posible que desee llamar directamente , ya que no hay nada especial en él como un método: x.__del__(). Obviamente, debe hacerlo solo si sabe que no le importa que lo llamen dos veces. O, como último recurso, puede redefinir este método utilizando

type(x).__del__ = my_safe_cleanup_method  
ilya n.
fuente
5
Dice que la función de CPython de eliminar un objeto inmediatamente después de que su recuento de referencias se reduzca a cero es un "detalle de implementación". No estoy convencido. ¿Puede proporcionar un enlace que respalde esa afirmación? (Quiero decir, la fuente en negrita es bastante convincente por sí sola, pero los enlaces están en segundo lugar ... :-)
Stuart Berg
14
Detalle de la implementación de CPython: CPython actualmente usa un esquema de conteo de referencias con detección retardada (opcional) de basura vinculada cíclicamente, ... Otras implementaciones actúan de manera diferente y CPython puede cambiar. ( docs.python.org/2/reference/datamodel.html )
ilya n.
¿Qué pasa __exit__en este contexto? ¿Se ejecuta después o antes __del__o en conjunto?
lony
1
¿"Puede que no suceda en absoluto" incluye cuándo finaliza el programa?
Andy Hayden
1
@AndyHayden: es __del__posible que los métodos no se ejecuten incluso al finalizar el programa, e incluso cuando se ejecutan al finalizar, escribir un __del__método que funcione correctamente incluso cuando el intérprete está ocupado autodestruyéndose a su alrededor requiere una codificación más cuidadosa que la que aplican muchos programadores. (La limpieza de CPython generalmente hace que los __del__métodos se ejecuten en el cierre del intérprete, pero aún hay casos en los que no es suficiente. Los subprocesos de demonio, los globales de nivel C y los objetos __del__creados en otro __del__pueden llevar a que los __del__métodos no se ejecuten).
user2357112 admite Monica
80

Escribí la respuesta para otra pregunta, aunque esta es una pregunta más precisa.

¿Cómo funcionan los constructores y destructores?

Aquí hay una respuesta ligeramente obstinada.

No lo use __del__. Esto no es C ++ ni un lenguaje creado para destructores. El __del__método realmente debería desaparecer en Python 3.x, aunque estoy seguro de que alguien encontrará un caso de uso que tenga sentido. Si necesita usarlo __del__, tenga en cuenta las limitaciones básicas según http://docs.python.org/reference/datamodel.html :

  • __del__se llama cuando el recolector de basura está recolectando los objetos, no cuando pierde la última referencia a un objeto y no cuando ejecuta del object.
  • __del__es responsable de llamar a cualquiera __del__en una superclase, aunque no está claro si esto está en orden de resolución de método (MRO) o simplemente llamando a cada superclase.
  • Tener un __del__medio de que el recolector de basura se rinda en detectar y limpiar cualquier enlace cíclico, como perder la última referencia a una lista enlazada. Puede obtener una lista de los objetos ignorados de gc.garbage. A veces puede usar referencias débiles para evitar el ciclo por completo. Esto se debate de vez en cuando: consulte http://mail.python.org/pipermail/python-ideas/2009-October/006194.html .
  • La __del__función puede hacer trampa, guardar una referencia a un objeto y detener la recolección de basura.
  • Las excepciones planteadas explícitamente en __del__se ignoran.
  • __del__complementa __new__mucho más que __init__. Esto se vuelve confuso. Consulte http://www.algorithm.co.il/blogs/programming/python-gotchas-1- del -is-not-the-opuesto-of- init / para obtener una explicación y trucos.
  • __del__no es un niño "muy querido" en Python. Notará que la documentación de sys.exit () no especifica si la basura se recolecta antes de salir, y hay muchos problemas extraños. Llamar a los __del__globales provoca problemas de pedidos extraños, por ejemplo, http://bugs.python.org/issue5099 . ¿Debería __del__llamar incluso si __init__falla? Consulte http://mail.python.org/pipermail/python-dev/2000-March/thread.html#2423 para ver un hilo largo.

Pero en la otra mano:

Y mi razón personal para que no me guste la __del__función.

  • Cada vez que alguien menciona el tema __del__se convierte en treinta mensajes de confusión.
  • Rompe estos elementos en el Zen de Python:
    • Mejor es simple que complicado.
    • Los casos especiales no son lo suficientemente especiales como para romper las reglas.
    • Los errores nunca deben pasar silenciosamente.
    • Ante la ambigüedad, rechace la tentación de adivinar.
    • Debe haber una, y preferiblemente solo una, forma obvia de hacerlo.
    • Si la implementación es difícil de explicar, es una mala idea.

Entonces, encuentre una razón para no usar __del__.

Charles Merriam
fuente
6
Incluso si la pregunta no es exactamente: por qué no deberíamos usar __del__, sino cómo llamar __del__, su respuesta es interesante.
2015
Gracias. A veces, la mejor idea es alejarse de las ideas horribles.
Charles Merriam
En otras noticias, olvidé mencionar que PyPy (un intérprete más rápido para aplicaciones de ejecución más prolongada) se romperá en del .
Charles Merriam
¡Gracias @Gloin por actualizar el enlace roto!
Charles Merriam
@CharlesMerriam Gracias usted por la respuesta!
Tom Burrows
13

El __del__método, se llamará cuando el objeto sea recolectado como basura. Sin embargo, tenga en cuenta que no se garantiza necesariamente que se llame. El siguiente código por sí solo no lo hará necesariamente:

del obj

La razón es que delsimplemente reduce el recuento de referencias en uno. Si algo más tiene una referencia al objeto, __del__no se llamará.

Sin __del__embargo, hay algunas advertencias sobre el uso . Por lo general, no suelen ser muy útiles. Me parece más bien que desea usar un método cercano o tal vez una declaración with .

Consulte la documentación de Python sobre __del__métodos .

Otra cosa a tener en cuenta: los __del__métodos pueden inhibir la recolección de basura si se usan en exceso. En particular, una referencia circular que tiene más de un objeto con un __del__método no obtendrá la recolección de basura. Esto se debe a que el recolector de basura no sabe a cuál llamar primero. Consulte la documentación sobre el módulo gc para obtener más información.

Jason Baker
fuente
8

El __del__método (¡tenga en cuenta la ortografía!) Se llama cuando su objeto finalmente se destruye. Técnicamente hablando (en cPython) es cuando no hay más referencias a su objeto, es decir, cuando sale del alcance.

Si desea eliminar su objeto y, por lo tanto, llamar al __del__método, use

del obj1

que eliminará el objeto (siempre que no haya otras referencias a él).

Te sugiero que escribas una clase pequeña como esta.

class T:
    def __del__(self):
        print "deleted"

E investigar en el intérprete de Python, por ejemplo

>>> a = T()
>>> del a
deleted
>>> a = T()
>>> b = a
>>> del b
>>> del a
deleted
>>> def fn():
...     a = T()
...     print "exiting fn"
...
>>> fn()
exiting fn
deleted
>>>   

Tenga en cuenta que jython y ironpython tienen reglas diferentes en cuanto a exactamente cuándo se elimina y __del__se llama al objeto. Sin __del__embargo, no se considera una buena práctica debido a esto y al hecho de que el objeto y su entorno pueden estar en un estado desconocido cuando se llama. Tampoco está absolutamente garantizado __del__que se llamará: el intérprete puede salir de varias formas sin eliminar todos los objetos.

Nick Craig-Wood
fuente
1
en comparación con stackoverflow.com/a/2452895/611007 y stackoverflow.com/a/1481512/611007 , use del obj1parece una mala idea en la que confiar.
n611x007
0

Como se mencionó anteriormente, la __del__funcionalidad es algo poco confiable. En los casos en que pueda parecer útil, considere utilizar los métodos __enter__y en su __exit__lugar. Esto le dará un comportamiento similar a la with open() as f: passsintaxis utilizada para acceder a los archivos. __enter__se llama automáticamente al entrar en el ámbito de with, mientras que __exit__se llama automáticamente al salir. Consulte esta pregunta para obtener más detalles.

somoria
fuente