order that destructors get called?

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Thu Apr 8 01:03:19 EDT 2010


En Wed, 07 Apr 2010 19:08:14 -0300, Brendan Miller <catphive at catphive.net>  
escribió:

> I'm used to C++ where destrcutors get called in reverse order of  
> construction
> like this:
>
> {
>     Foo foo;
>     Bar bar;
>
>     // calls Bar::~Bar()
>     // calls Foo::~Foo()
> }

That behavior is explicitly guaranteed by the C++ language. Python does  
not have such guarantees -- destructors may be delayed an arbitrary amount  
of time, or even not called at all.
In contrast, Python does have a `try/finally` construct, and the `with`  
statement. If Foo and Bar implement adequate __enter__ and __exit__  
methods, the above code would become:

with Foo() as foo:
   with Bar() as bar:
     # do something

On older Python versions it is more verbose:

foo = Foo()
try:
   bar = Bar()
   try:
     # do something
   finally:
     bar.release_resources()
finally:
   foo.release_resources()

> I'm writing a ctypes wrapper for some native code, and I need to manage  
> some
> memory. I'm wrapping the memory in a python class that deletes the  
> underlying
>  memory when the python class's reference count hits zero.

If the Python object lifetime is tied to certain lexical scope (like the  
foo,bar local variables in your C++ example) you may use `with` or  
`finally` as above.
If some other object with a longer lifetime keeps a reference, see below.

> When doing this, I noticed some odd behaviour. I had code like this:
>
> def delete_my_resource(res):
>     # deletes res
>
> class MyClass(object):
>     def __del__(self):
>         delete_my_resource(self.res)
>
> o = MyClass()
>
> What happens is that as the program shuts down, delete_my_resource is  
> released
> *before* o is released. So when __del__ get called, delete_my_resource  
> is now
> None.

Implementing __del__ is not always a good idea; among other things, the  
garbage collector cannot break a cycle if any involved object contains a  
__del__ method. [1]
If you still want to implement __del__, keep a reference to  
delete_my_resource in the method itself:

      def __del__(self,
                    delete_my_resource=delete_my_resource):
          delete_my_resource(self.res)

(and do the same with any global name that delete_my_resource itself may  
reference).

The best approach is to store a weak reference to foo and bar somewhere;  
weak references are notified right before the referent is destroyed. [4]

And last, if you want to release something when the program terminates,  
you may use the atexit module.

> What I'm wondering is if there's any documented order that reference  
> counts
> get decremented when a module is released or when a program terminates.

Not much, as Stephen Hansen already told you; but see the comments in  
PyImport_Cleanup function in import.c [2] and in _PyModule_Clear in  
moduleobject.c [3]
Standard disclaimer: these undocumented details only apply to the current  
version of CPython, may change in future releases, and are not applicable  
at all to other implementations. So it's not a good idea to rely on this  
behavior.

[1] http://docs.python.org/reference/datamodel.html#object.__del__
[2] http://svn.python.org/view/python/trunk/Python/import.c?view=markup
[3]  
http://svn.python.org/view/python/trunk/Objects/moduleobject.c?view=markup
[4] http://docs.python.org/library/weakref.html

-- 
Gabriel Genellina




More information about the Python-list mailing list