Class destructor -- strange behaviour

S

Spes

Hi,

I have this simple code:
| #!/usr/bin/python
| import codecs
| import re
| from copy import deepcopy
|
| class MyClass(object):
| def __del__(self):
| deepcopy(1)
|
| x=MyClass()

but I get an error:
| Exception exceptions.TypeError: "'NoneType' object is not callable"
in <bound method MyClass.__del__ of <__main__.MyClass object at
0x6fcf0>> ignored

The problem disappears if I do anything of this:
1. change
- from copy import deepcopy
+ import copy
and call directly copy.deepcopy(1)

or
2. don't store object to variable `x'

or
3. don't import module `re'

The first solution is OK, but I would like to know why it behaves so
strange. We have tested on:

- Mac OS X Tiger for PPC
Python 2.3.5 (#1, Mar 20 2005, 20:38:20)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1809)] on darwin

- Linux 64bit and 32bit
Python 2.4.4 (#1, Oct 30 2007, 14:31:50)
[GCC 4.1.2 (Gentoo 4.1.2 p1.0.2)] on linux2
Python 2.5 (r25:51908, Jan 12 2007, 13:57:15)
[GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2

Thanks for the explanation,
Vlasta
 
M

MonkeeSage

Hi,

I have this simple code:
| #!/usr/bin/python
| import codecs
| import re
| from copy import deepcopy
|
| class MyClass(object):
| def __del__(self):
| deepcopy(1)
|
| x=MyClass()

but I get an error:
| Exception exceptions.TypeError: "'NoneType' object is not callable"
in <bound method MyClass.__del__ of <__main__.MyClass object at
0x6fcf0>> ignored

The problem disappears if I do anything of this:
1. change
- from copy import deepcopy
+ import copy
and call directly copy.deepcopy(1)

or
2. don't store object to variable `x'

or
3. don't import module `re'

The first solution is OK, but I would like to know why it behaves so
strange. We have tested on:

- Mac OS X Tiger for PPC
Python 2.3.5 (#1, Mar 20 2005, 20:38:20)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1809)] on darwin

- Linux 64bit and 32bit
Python 2.4.4 (#1, Oct 30 2007, 14:31:50)
[GCC 4.1.2 (Gentoo 4.1.2 p1.0.2)] on linux2
Python 2.5 (r25:51908, Jan 12 2007, 13:57:15)
[GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2

Thanks for the explanation,
Vlasta

I can't explain why, but it also works if you call del x manually
before the script exits.

Regards,
Jordan
 
G

Gabriel Genellina

I have this simple code:
| #!/usr/bin/python
| import codecs
| import re
| from copy import deepcopy
|
| class MyClass(object):
| def __del__(self):
| deepcopy(1)
|
| x=MyClass()

but I get an error:
| Exception exceptions.TypeError: "'NoneType' object is not callable"
in <bound method MyClass.__del__ of <__main__.MyClass object at
0x6fcf0>> ignored

Good question!
First: what does the message mean? Somewhere inside __del__, you are
trying to call an object, and that object is None. The only possible cause
is deepcopy being None, and you can confirm that easily: adding a 'print
deepcopy' just above the call, shows that deepcopy is actually None inside
__del__.

This is related to the Python finalization process. At some stage, Python
clears the __main__ module's namespace: not by removing the names, but
assigning None to them, and finally removing __main__ from sys.modules.
(The same thing is done, in turn, for all other modules, ending with sys
and __builtin__). That means that you can't trust any globals inside a
destructor (see the second big warning at
http://docs.python.org/ref/customization.html).
You can avoid this problem (partially) storing a reference to deepcopy
into the *local* namespace:

def __del__(self,
deepcopy=deepcopy):
...
(this works as long as the deepcopy function itself does not reference
other globals that might not be still available)
The problem disappears if I do anything of this:
1. change
- from copy import deepcopy
+ import copy
and call directly copy.deepcopy(1)

or
2. don't store object to variable `x'

or
3. don't import module `re'

Back to the finalization process, the names in the __main__ module
namespace are set to None IN THE ORDER IN WHICH THEY ARE ENCOUNTERED (in
fact, this is done in two stages: first names starting with '_', then all
remaining names, but excluding '__builtins__'). So, if "deepcopy" (as a
dictionary key) comes before "x" (the variable name) when you iterate the
module's dict, deepcopy will be set to None before x, and when x.__del__
is called it will fail.

Now try your examples again and print globals.keys() for each variant, and
notice the name ordering. If you can arrange things so 'x' (or whatever
variable name you choose) comes before than 'deepcopy' in globals.keys(),
you won't get any error.
Note that, for example, types are irrelevant: you can replace 'import re'
with 're=0' and get the same results.
The first solution is OK, but I would like to know why it behaves so
strange. We have tested on:

I hope my explanation is understandable. If you want to know the gory
details, see Py_Finalize in pythonrun.c, PyImport_Cleanup in import.c and
_PyModule_Clear in moduleobject.c
- Mac OS X Tiger for PPC
Python 2.3.5 (#1, Mar 20 2005, 20:38:20)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1809)] on darwin

- Linux 64bit and 32bit
Python 2.4.4 (#1, Oct 30 2007, 14:31:50)
[GCC 4.1.2 (Gentoo 4.1.2 p1.0.2)] on linux2
Python 2.5 (r25:51908, Jan 12 2007, 13:57:15)
[GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2

Mmm, you got the same results on 64 bit too? I would have expected that,
having a 64 bits hash value, key ordering inside a dictionary would be
different. Perhaps it's just a coincidence.
 
J

Jason

Hi,

I have this simple code:
| #!/usr/bin/python
| import codecs
| import re
| from copy import deepcopy
|
| class MyClass(object):
| def __del__(self):
| deepcopy(1)
|
| x=MyClass()

but I get an error:
| Exception exceptions.TypeError: "'NoneType' object is not callable"
in <bound method MyClass.__del__ of <__main__.MyClass object at
0x6fcf0>> ignored

The problem disappears if I do anything of this:
1. change
- from copy import deepcopy
+ import copy
and call directly copy.deepcopy(1)

or
2. don't store object to variable `x'

or
3. don't import module `re'

The first solution is OK, but I would like to know why it behaves so
strange. We have tested on:

- Mac OS X Tiger for PPC
Python 2.3.5 (#1, Mar 20 2005, 20:38:20)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1809)] on darwin

- Linux 64bit and 32bit
Python 2.4.4 (#1, Oct 30 2007, 14:31:50)
[GCC 4.1.2 (Gentoo 4.1.2 p1.0.2)] on linux2
Python 2.5 (r25:51908, Jan 12 2007, 13:57:15)
[GCC 4.0.2 20051125 (Red Hat 4.0.2-8)] on linux2

Thanks for the explanation,
Vlasta

Gabriel nailed the problem down. Another solution is to explicitly
import module names in your destructor, ie:

def __del__(self):
from copy import deepcopy
deepcopy(1)

This guarantees that your destructor has "deepcopy" binding that you
want, even if the name has already been reassigned to None in your
current module by the interpreter as it is shutting down.

--Jason
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top