todos - ¿Cómo forzar la eliminación de un objeto python?
eliminar un elemento de una matriz python (4)
- Agregue un controlador de salida que cierre todas las barras.
-
__del__()
cuando el número de referencias a un objeto llega a 0 mientras la VM todavía está ejecutándose. Esto puede ser causado por el GC. - Si
__init__()
genera una excepción, entonces se supone que el objeto está incompleto y__del__()
no se invocará.
Tengo curiosidad acerca de los detalles de __del__
en python, cuándo y por qué se debe usar y para qué no se debe usar. Aprendí por las malas que no es realmente lo que ingenuamente se esperaría de un destructor, ya que no es lo opuesto a __new__
/ __init__
.
class Foo(object):
def __init__(self):
self.bar = None
def open(self):
if self.bar != ''open'':
print ''opening the bar''
self.bar = ''open''
def close(self):
if self.bar != ''closed'':
print ''closing the bar''
self.bar = ''close''
def __del__(self):
self.close()
if __name__ == ''__main__'':
foo = Foo()
foo.open()
del foo
import gc
gc.collect()
Vi en la documentación que no está garantizado __del__()
se solicitan métodos para objetos que aún existen cuando el intérprete sale.
- ¿Cómo se puede garantizar que para cualquier instancia de
Foo
exista cuando el intérprete se cierre, la barra esté cerrada? - en el fragmento de código anterior, ¿se cierra la barra en
del foo
o engc.collect()
... o ninguno? si desea un control más preciso de esos detalles (por ejemplo, la barra debe estar cerrada cuando el objeto no está referenciado), ¿cuál es la forma habitual de implementar eso? - cuando
__del__
es llamado ¿se garantiza que__init__
ya ha sido llamado? ¿Qué pasa si el__init__
levantado?
¿Quizás estás buscando un administrador de contexto ?
>>> class Foo(object):
... def __init__(self):
... self.bar = None
... def __enter__(self):
... if self.bar != ''open'':
... print ''opening the bar''
... self.bar = ''open''
... def __exit__(self, type_, value, traceback):
... if self.bar != ''closed'':
... print ''closing the bar'', type_, value, traceback
... self.bar = ''close''
...
>>>
>>> with Foo() as f:
... # oh no something crashes the program
... sys.exit(0)
...
opening the bar
closing the bar <type ''exceptions.SystemExit''> 0 <traceback object at 0xb7720cfc>
En general, para asegurarse de que ocurra algo sin importar qué, use
from exceptions import NameError
try:
f = open(x)
except ErrorType as e:
pass # handle the error
finally:
try:
f.close()
except NameError: pass
finally
bloques se ejecutarán independientemente de que haya o no un error en el bloque try
, y de si hay o no un error en el manejo de errores que tiene lugar en los bloques except
. Si no maneja una excepción que se genera, aún se generará después de que finally
haya ejecutado el bloque finally.
La forma general de asegurarse de que un archivo esté cerrado es usar un "administrador de contexto".
http://docs.python.org/reference/datamodel.html#context-managers
with open(x) as f:
# do stuff
Esto cerrará automáticamente f
.
Para su pregunta n. ° 2, la bar
se cierra inmediatamente cuando el recuento de referencias llega a cero, por lo tanto, en el caso de que no haya otras referencias.
Los objetos NO son creados por __init__
, son creados por __new__
.
http://docs.python.org/reference/datamodel.html#object. nuevo
Cuando haces foo = Foo()
dos cosas están sucediendo realmente, primero se está creando un nuevo objeto, __new__
, luego se está inicializando, __init__
. Así que no hay forma de que puedas llamar del foo
antes de que ambos pasos hayan tenido lugar. Sin embargo, si hay un error en __init__
, todavía se llamará a __del__
porque el objeto ya se creó en __new__
.
Editar: se corrige cuando se produce una eliminación si el recuento de referencias disminuye a cero.
La forma de cerrar recursos son los gestores de contexto, también conocido como la declaración with
:
class Foo(object):
def __init__(self):
self.bar = None
def __enter__(self):
if self.bar != ''open'':
print ''opening the bar''
self.bar = ''open''
return self # this is bound to the `as` part
def close(self):
if self.bar != ''closed'':
print ''closing the bar''
self.bar = ''close''
def __exit__(self, *err):
self.close()
if __name__ == ''__main__'':
with Foo() as foo:
print foo, foo.bar
salida:
opening the bar
<__main__.Foo object at 0x17079d0> open
closing the bar
2) Los objetos de Python se eliminan cuando su recuento de referencias es 0. En su ejemplo, del foo
elimina la última referencia, por lo que __del__
se llama instantáneamente. El GC no tiene parte en esto.
class Foo(object):
def __del__(self):
print "deling", self
if __name__ == ''__main__'':
import gc
gc.disable() # no gc
f = Foo()
print "before"
del f # f gets deleted right away
print "after"
salida:
before
deling <__main__.Foo object at 0xc49690>
after
El gc
no tiene nada que ver con eliminar tu y la mayoría de los otros objetos. Está ahí para limpiar cuando el recuento de referencia simple no funciona, debido a referencias propias o referencias circulares:
class Foo(object):
def __init__(self, other=None):
# make a circular reference
self.link = other
if other is not None:
other.link = self
def __del__(self):
print "deling", self
if __name__ == ''__main__'':
import gc
gc.disable()
f = Foo(Foo())
print "before"
del f # nothing gets deleted here
print "after"
gc.collect()
print gc.garbage # The GC knows the two Foos are garbage, but won''t delete
# them because they have a __del__ method
print "after gc"
# break up the cycle and delete the reference from gc.garbage
del gc.garbage[0].link, gc.garbage[:]
print "done"
salida:
before
after
[<__main__.Foo object at 0x22ed8d0>, <__main__.Foo object at 0x22ed950>]
after gc
deling <__main__.Foo object at 0x22ed950>
deling <__main__.Foo object at 0x22ed8d0>
done
3) Veamos:
class Foo(object):
def __init__(self):
raise Exception
def __del__(self):
print "deling", self
if __name__ == ''__main__'':
f = Foo()
da:
Traceback (most recent call last):
File "asd.py", line 10, in <module>
f = Foo()
File "asd.py", line 4, in __init__
raise Exception
Exception
deling <__main__.Foo object at 0xa3a910>
Los objetos se crean con __new__
luego pasan a __init__
como self
. Después de una excepción en __init__
, el objeto normalmente no tendrá un nombre (es decir, la parte f =
no se ejecuta), por lo que su recuento de ref es 0. Esto significa que el objeto se elimina normalmente y se llama __del__
.