Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python how to ensure that __del__() method of an object is called before the module dies?

Earlier today I asked this question about the __del__() method of an object which uses an imported module. The problem was that __del__() wants to make use of the module os, but sometimes (not always) the module has already been deleted. I was told that when a Python program terminates, the order in which objects and modules are deleted can be random, and is undefined. However, for my application, I really need to make sure that an object (instance of a class I created) gets deleted before the module (in which the object is instantiated) gets deleted. Is there a way of doing this?

like image 211
Ray Avatar asked Feb 20 '13 17:02

Ray


2 Answers

As we told you before, you really shouldn't rely on __del__ being called when the interpreter exits. There are two options for doing this properly:

The first is atexit

import os
import atexit

class Logger(object):   
    def on_exit(self):
        print "os: %s." % os

logger = Logger()
atexit.register(logger.on_exit)

This makes sure that your logger gets finalized at exit.


*Reading a little more into your problem, since you plan on having a single instance bound to a module which defines the instances's class, the context manager solution below won't work for this since there's no way to stay in the context for the entire execution of your program. You'll need to use atexit.register. However, from the standpoint of program design, I would much prefer to use a context manager to manage my resources than atexit.register if restructuring the code would allow it.

The second (better*) way to do it is make your class a context manager which executes the cleanup code when you exit the context. Then your code would look like:

import os
class Logger(object):
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        print "os:",str(os)

with Logger() as logger:
    #do something ...
like image 90
mgilson Avatar answered Sep 30 '22 17:09

mgilson


Attach a reference to the function with a keyword parameter:

import os

class Logger(object):    
    def __del__(self, os=os):
        print "os: %s." %os

or bind it to a class attribute:

import os

class Logger(object):
    def __init__(self):
        self.os = os

    def __del__(self):
        print "os: %s." % self.os

By creating a local (enough) reference, Python will not look up os as a global, but with a local reference, which are stored with the instance or with the function itself, respectively.

Globals are looked up at runtime (which is why the __del__ function in your other question fails if os has already been cleaned up), while a function local is cleared up together with the function object, or in case of a instance attribute, with the instance.

like image 32
Martijn Pieters Avatar answered Sep 30 '22 18:09

Martijn Pieters