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.