xticks barplot python destructor

barplot - ¿Cómo limpio correctamente un objeto de Python?



pandas plot (9)

Aquí es un esqueleto de trabajo mínimo:

class SkeletonFixture: def __init__(self): pass def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): pass def method(self): pass with SkeletonFixture() as fixture: fixture.method()

Importante: volver a ti mismo

Si eres como yo, y pasas por alto la parte de return self (de la respuesta correcta de Clint Miller ), estarás mirando esta tontería:

Traceback (most recent call last): File "tests/simplestpossible.py", line 17, in <module> fixture.method() AttributeError: ''NoneType'' object has no attribute ''method''

Pasé medio día en esto. Espero que ayude a la siguiente persona.

class Package: def __init__(self): self.files = [] # ... def __del__(self): for file in self.files: os.unlink(file)

__del__(self) anterior falla con una excepción AttributeError. Entiendo que Python no garantiza la existencia de "variables globales" (¿datos de miembros en este contexto?) Cuando se __del__() . 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?


Como un apéndice a la respuesta de Clint , puede simplificar PackageResource usando contextlib.contextmanager :

@contextlib.contextmanager def packageResource(): class Package: ... package = Package() yield package package.cleanup()

Alternativamente, aunque probablemente no sea Pythonic, puede anular el Package.__new__ :

class Package(object): def __new__(cls, *args, **kwargs): @contextlib.contextmanager def packageResource(): # adapt arguments if superclass takes some! package = super(Package, cls).__new__(cls) package.__init__(*args, **kwargs) yield package package.cleanup() def __init__(self, *args, **kwargs): ...

y simplemente usar with Package(...) as package .

Para contextlib.closing , close su función de limpieza y use contextlib.closing , en cuyo caso puede usar la clase Package sin modificar a través with contextlib.closing(Package(...)) o anular su __new__ a la más simple

class Package(object): def __new__(cls, *args, **kwargs): package = super(Package, cls).__new__(cls) package.__init__(*args, **kwargs) return contextlib.closing(package)

Y este constructor se hereda, por lo que simplemente puede heredar, por ejemplo,

class SubPackage(Package): def close(self): pass


Creo que el problema podría estar en __init__ si hay más código del que se muestra?

__del__ se llamará incluso cuando __init__ no se haya ejecutado correctamente o se haya __init__ una excepción.

Fuente


La forma estándar es usar atexit.register :

# package.py import atexit import os class Package: def __init__(self): self.files = [] atexit.register(self.cleanup) def cleanup(self): print("Running cleanup...") for file in self.files: print("Unlinking file: {}".format(file)) # os.unlink(file)

Pero debe tener en cuenta que esto persistirá en todas las instancias creadas de Package hasta que Python finalice.

Demo usando el código anterior guardado como package.py :

$ python >>> from package import * >>> p = Package() >>> q = Package() >>> q.files = [''a'', ''b'', ''c''] >>> quit() Running cleanup... Unlinking file: a Unlinking file: b Unlinking file: c Running cleanup...


No creo que sea posible eliminar miembros de instancia antes de que se __del__ . Mi conjetura sería que la razón de tu AttributeError en particular está en otra parte (tal vez, por error, elimines self.file en otra parte).

Sin embargo, como señalaron los demás, debe evitar usar __del__ . La razón principal de esto es que las instancias con __del__ no se recolectarán como basura (solo se liberarán cuando su refcount 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. (Aunque podría estar equivocado acerca de todo esto, tendría que leer los documentos de gc otra vez, pero estoy bastante seguro de que funciona así).


Parece que la forma idiomática de hacerlo es proporcionar un método close() (o similar) y llamarlo explícitamente.


Recomiendo usar Python''s with declaración para administrar recursos que necesitan ser limpiados. El problema con el uso de una declaración de close() explícita es que debe preocuparse por que las personas se olviden de llamarla o se olviden de colocarla en un bloque final para evitar una fuga de recursos cuando se produce una excepción.

Para usar la instrucción with , cree una clase con los siguientes métodos:

def __enter__(self) def __exit__(self, exc_type, exc_value, traceback)

En tu ejemplo anterior, usarías

class Package: def __init__(self): self.files = [] def __enter__(self): return self # ... def __exit__(self, exc_type, exc_value, traceback): for file in self.files: os.unlink(file)

Luego, cuando alguien quisiera usar tu clase, harían lo siguiente:

with Package() as package_obj: # use package_obj

La variable package_obj será una instancia de tipo Package (es el valor devuelto por el método __enter__ ). Su método __exit__ se llamará automáticamente, independientemente de si se produce o no una excepción.

Incluso podrías llevar este enfoque un paso más allá. En el ejemplo anterior, alguien aún podría crear una instancia del Paquete usando su constructor sin usar la cláusula with . No quieres que eso suceda. Puede solucionar esto creando una clase PackageResource que define los métodos __enter__ y __exit__ . Luego, la clase de Paquete se definirá estrictamente dentro del método __enter__ y se devolverá. De esa manera, la persona que llama nunca podría crear una instancia de la clase Package sin usar una instrucción with :

class PackageResource: def __enter__(self): class Package: ... self.package_obj = Package() return self.package_obj def __exit__(self, exc_type, exc_value, traceback): self.package_obj.cleanup()

Usaría esto de la siguiente manera:

with PackageResource() as package_obj: # use package_obj


Simplemente envuelva su destructor con una declaración try / except y no lanzará una excepción si sus globales ya están eliminados.

Editar

Prueba esto:

from weakref import proxy class MyList(list): pass class Package: def __init__(self): self.__del__.im_func.files = MyList([1,2,3,4]) self.files = proxy(self.__del__.im_func.files) def __del__(self): print self.__del__.im_func.files

Rellenará la lista de archivos en la función del que se garantiza que existe en el momento de la llamada. El proxy débil es para evitar que Python, o usted mismo elimine la variable self.files de alguna manera (si se elimina, no afectará a la lista de archivos original). Si no es el caso de que se esté eliminando aunque haya más referencias a la variable, puede eliminar la encapsulación de proxy.