Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I correctly clean up a Python object?

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

__del__(self) above fails with an AttributeError exception. I understand Python doesn't guarantee the existence of "global variables" (member data in this context?) when __del__() is invoked. If that is the case and this is the reason for the exception, how do I make sure the object destructs properly?

like image 226
wilhelmtell Avatar asked May 14 '09 19:05

wilhelmtell


People also ask

How do you clean up Python?

Cleanup happens to globals by setting them to None. The locals self destruct at the end of the session. The function __del__ called by Python sets the globals to None.

How do you dispose an object in Python?

You cannot manually destroy objects in Python, Python uses automatic memory management. When an object is no longer referenced, it is free to be garbage collected, in CPython, which uses reference counting, when a reference count reaches zero, an object is reclaimed immediately.

What is __ del __ in Python?

The __del__() method is a known as a destructor method in Python. It is called when all references to the object have been deleted i.e when an object is garbage collected.

What is __ new __ in Python?

The __new__() is a static method of the object class. When you create a new object by calling the class, Python calls the __new__() method to create the object first and then calls the __init__() method to initialize the object's attributes.


2 Answers

I'd recommend using Python's with statement for managing resources that need to be cleaned up. The problem with using an explicit close() statement is that you have to worry about people forgetting to call it at all or forgetting to place it in a finally block to prevent a resource leak when an exception occurs.

To use the with statement, create a class with the following methods:

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

In your example above, you'd use

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) 

Then, when someone wanted to use your class, they'd do the following:

with Package() as package_obj:     # use package_obj 

The variable package_obj will be an instance of type Package (it's the value returned by the __enter__ method). Its __exit__ method will automatically be called, regardless of whether or not an exception occurs.

You could even take this approach a step further. In the example above, someone could still instantiate Package using its constructor without using the with clause. You don't want that to happen. You can fix this by creating a PackageResource class that defines the __enter__ and __exit__ methods. Then, the Package class would be defined strictly inside the __enter__ method and returned. That way, the caller never could instantiate the Package class without using a with statement:

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() 

You'd use this as follows:

with PackageResource() as package_obj:     # use package_obj 
like image 54
Clint Miller Avatar answered Oct 16 '22 10:10

Clint Miller


The standard way is to use 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) 

But you should keep in mind that this will persist all created instances of Package until Python is terminated.

Demo using the code above saved as 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... 
like image 32
ostrokach Avatar answered Oct 16 '22 10:10

ostrokach