python python-3.x garbage-collection destructor weak-references

python - ¿Pueden las devoluciones de llamada `weakref` reemplazar a`__del__`?



python-3.x garbage-collection (2)

¿Hay algún obstáculo que evite que weakref haga todo lo que __del__ hace pero con garantías mucho más fuertes (p. Ej., finalize garantías de que la llamada se realizará antes de que salga el intérprete, y el orden de las llamadas esté bien definido, etc.)?

Parece que en el pasado lejano se pensó que una weakref llevaría eventualmente a la eliminación de __del__ del lenguaje.

¿Qué evitó que esto sucediera?

Parece que hay pocos casos de uso para __del__ , y todos los que conozco parecen funcionar al menos tan bien (y, por lo general, mucho mejor) con weakref llamada weakref.finalize o weakref.finalize .

Actualizar:

Con PEP 442 mejorando dramáticamente el comportamiento de __del__ , y las preocupaciones con weakref mencionadas por @gz y @ user2357112, me pregunto si el lenguaje generalmente se está moviendo hacia hacer __del__ más confiable, o hacia el uso de weakref lugar de __del__ , o ambos.


Hay una razón un tanto pragmática: __del__ todavía existe. Varias mejoras significativas de weakref , incluida la finalize , fueron nuevas en Python 3.4 . Por lo tanto, reemplazar __del__ con mejores puntos débiles faltó a la ventana para cambiar los cambios de idioma con py3k.

Creo que la mayoría de los usos pueden ser reemplazados por la funcionalidad débil de base, pero me sorprende esta observación de Richard Oudkerk en el número 15528 donde se propuso e implementó la finalize :

[Las devoluciones de llamada de Weakref] son ​​de bajo nivel, y descubrir cómo usarlas correctamente requiere un poco de rascarse la cabeza. Uno debe encontrar un lugar donde guardar la referencia débil hasta que el referente esté muerto, y sin mantenerlo accidentalmente vivo. Luego, uno debe asegurarse de que la devolución de llamada libere a la referencia débil (sin dejar ningún resto de ciclos de ref).

Cuando es una opción, usar un método __del__ es mucho menos complicado.

De todos modos, ¿quizás debería plantearse nuevamente la pregunta cuando se está considerando Python 4? ;)


La respuesta a la pregunta depende realmente del caso de uso y también sugiere que considere que las diferentes implementaciones de intérpretes de Python no tienen el mismo comportamiento de GC que CPython.

PyPy, en particular, no llama a __del__ tan pronto como un objeto se elimina o queda fuera del alcance, pero "algún tiempo después". Por lo tanto, el código que se basa en el comportamiento __del__ de CPython se interrumpirá en PyPy y otros intérpretes alternativos.

Lo que recomendaría es utilizar __enter__ y __exit__ junto con la palabra clave with . Por ejemplo

from __future__ import print_function class MyClass(object): def __enter__(self): print("Entering") def __exit__(self, exc_type, exc_val, exc_tb): print("Exiting") # ''with MyClass()'' can be used but want to show that # code in exit is executed regardless of object # life time obj = MyClass() with obj: print("Stuff happening in between")

El resultado es:

Entering Stuff happening in between Exiting

El orden anterior de las declaraciones está garantizado y no depende del comportamiento del GC.

Las cosas que se deben hacer tan pronto como el código en el bloque with complete se pueden ingresar en __exit__ , como las cosas que normalmente se pondrían en un destructor: borrar los manejadores de archivos, liberar bloqueos, etc.

Una supresión posterior en el objeto, o que salga de su alcance, borrará la referencia del objeto eventualmente, nuevamente, dependiendo de la implementación del intérprete, pero las cosas que deben hacerse de inmediato es mejor no confiar en ese comportamiento.