__del__: when to use it? What happens when you SystemExit/NameError wrt del? Method vs function calls.

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Mar 7 00:56:45 EST 2016


On Monday 07 March 2016 14:27, Veek. M wrote:

> 1. What are the rules for using __del__ besides: 'don't use it'.

__del__ needs to be defined in a class to be called.

It will be called *at some point* when the instance is about to be garbage 
collected. There is no guarantee when that will be: it might be instantly, 
or a week later.

It may not be called at all, under some obscure circumstances which I don't 
remember.

The presence of a __del__ method may prevent the garbage collector from 
breaking reference cycles, e.g. if A refers to B, and B refers to A, under 
some circumstances if A or B have a __del__ method, the GC may not be able 
to break the cycle and neither object will be collected.

At interpreter shutdown, the __del__ method of objects may be called *after* 
the resources they are relying on have been deleted. See below.

All of these factors are version- and implementation-dependent. In 
particular, remember that IronPython and Jython are based on the .Net CLR 
and JVM, so will use completely different garbage collectors than the 
CPython garbage collector, and so behave slightly differently.


> 2. What happens when I SystemExit? __del__ and gc are not invoked when I
> SystemExit and there's a circular reference - but why? The OS is going
> to reclaim the memory anyways so why be finicky about circular
> references - why can't we go ahead and call __dell_ and run gc?

I'm sorry, I don't understand the GC well enough to answer that question.


> 3.
> import foo
> def __del__(self, foo=foo):
>   foo.bar()
> 
> What happens here to prevent a NameError? Apparently without the foo=foo
> a NameError can occur? But why? import foo creates a reference to the
> object anyways so it's refcount will be above 0 anyways till __del__ is
> called.

I assume that you intended the __del__ method to be inside a class.

Let us see what happens without the local reference:


import foo

class X:
    def __del__(self):
        foo.bar()

instance = X()
sys.exit()



The `import foo` line creates a module level reference to foo. But during 
interpreter shutdown, the module references may be deleted before the 
__del__ method is called. If that happens, then the code `foo.bar()` will do 
a global lookup for `foo`, and not find it, and you will get a NameError.

The way to fix this is to make the class hold onto a reference to foo. Then, 
even if the module references are deleted, the class reference won't be. 
Either of these ways should work:


class X:
    foo = foo  # make foo an attribute of the class itself
    def __del__(self):
        self.foo.bar()


class X:
    def __del__(self, foo=foo):  # make foo a local variable of the method
        foo.bar()



> 4. also, are method calls more efficient than function calls?

No. For technical reasons, methods are a thin wrapper around actual 
functions (the "descriptor protocol"). Although it is very fast to create 
that wrapper, it is not instantaneous, so methods are not faster than 
function calls. The difference is very small though, so it is very rare that 
you should need to care about the speed difference. (It is usually dwarfed 
by the function body itself.)

Also, remember that attribute lookups are resolved at runtime:

instance.attr.spam.ham.eggs.cheese.foo.bar.baz.foobar()


needs to do nine lookups at runtime. This includes method calls, so try to 
avoid long chains of "dots". If you are used to Java, you may need to change 
your style of programming:

http://dirtsimple.org/2004/12/python-is-not-java.html




-- 
Steve




More information about the Python-list mailing list