python's newbie question


T

tpochep

Hi. I have some strange problem (this is usual for newbies :) ):

#!/usr/bin/python
#sample.py

class Base1:
def __init__(self):
print "Base1.__init__", self
def __del__(self):
print "Base1.__del__", self

class Base2:
def __init__(self):
print "Base2.__init__", self
def __del__(self):
print "Base2.__del__", self

class Derived(Base1, Base2):
def __init__(self):
print "Derived.__init__:"
Base1.__init__(self)
Base2.__init__(self)
def __del__(self):
print "Derived.__del__:"
Base1.__del__(self)
Base2.__del__(self)

print "begin..."
d = Derived()
b = d
print "end..."

The output of this script:

begin...
Derived.__init__:
Base1.__init__ <__main__.Derived instance at 0x1869adcc>
Base2.__init__ <__main__.Derived instance at 0x1869adcc>
end...
Derived.__del__:
Base1.__del__ <__main__.Derived instance at 0x1869adcc>
Base2.__del__ <__main__.Derived instance at 0x1869adcc>

If I change this program a little:

#!/usr/bin/python
#skipped....

print "begin..."
d = Derived()
b1 = d #Here
print "end..."

The output is:

begin...
Derived.__init__:
Base1.__init__ <__main__.Derived instance at 0x1869adcc>
Base2.__init__ <__main__.Derived instance at 0x1869adcc>
end...
Derived.__del__:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute '__del__'" in <bound method
Derived.__del__ of <__main__.Derived instance at 0x1869adcc>> ignored

I tried different names instead of b1, sometimes programm works,
sometimes I have an exception. So it's clear, that I've done some
stupid mistake, but it's not clear for me, python's newbie, where :)
 
Ad

Advertisements

F

Fredrik Lundh

begin...
Derived.__init__:
Base1.__init__ <__main__.Derived instance at 0x1869adcc>
Base2.__init__ <__main__.Derived instance at 0x1869adcc>
end...
Derived.__del__:
Exception exceptions.AttributeError: "'NoneType' object has no
attribute '__del__'" in <bound method
Derived.__del__ of <__main__.Derived instance at 0x1869adcc>> ignored

I tried different names instead of b1, sometimes programm works,
sometimes I have an exception. So it's clear, that I've done some
stupid mistake, but it's not clear for me, python's newbie, where :)

The real mistake here is to use __del__ in your program; don't do that, unless
you have really good reasons (and they are few and not very common). In most
cases, you can just rely on Python's garbage collector; it'll clean up after you all
by itself, most of the time.

The error you're seeing is due to the fact that Python attempts to destroy every-
thing during shutdown, and this process includes clearing out the contents of all
modules in the program. Your object is simply destroyed *after* the module it's
defined in is cleared. To work around this, you need to keep references to all
objects you might end up needing in your object. (Or just drop the __del__
methods. Chances are that you don't really need them.) For more on module
cleanup, see this old Guido essay:

http://www.python.org/doc/essays/cleanup/

</F>
 
Ad

Advertisements

D

Duncan Booth

I tried different names instead of b1, sometimes programm works,
sometimes I have an exception. So it's clear, that I've done some
stupid mistake, but it's not clear for me, python's newbie, where :)

Not a stupid mistake, although as you come to use Python more you will
learn that you need __del__ methods very rarely if at all. This is just as
well as writing reliable __del__ methods can be non-trivial.

The problem you have is that your __del__ method is being called as Python
tidies up before exiting. It does this by going systematically through all
the modules and deleting all of their global variables. If you are using
__del__ you have to be aware that by the time it gets called not all global
variables still exist.

The order in which the globals are deleted isn't defined, in this case it
looks like Base1 has been deleted before Derived, but that won't always be
the case.

One option is to use new-style classes and use super() to pass the call up
to the base classes (super is a builtin, and builtins should still be
around when your code is being cleaned up). Another option would be to
access the base classes through Derived.__bases__.

Probably the best solution is to remove any objects which required cleanup
from your class hierarchy. e..g if Derived has a __del__ method because it
needs to close a database, have a separate object representing the database
which does its own cleanup. Then Derived doesn't need to call
self.database.close() because that will happen automatically when
self.database is destroyed, so Derived won't need a __del__.

Here's your code pushing the __del__ method out of the class hierarchy. But
watch out: if you pass self as an argument to printer then nothing gets
destroyed at all as the program exits, and of course the order in which the
__del__ prints appear is now undefined.

class printer(object):
def __init__(self, *args):
self.msg = args
def __del__(self):
print ' '.join(self.msg)

class Base1:
def __init__(self):
print "Base1.__init__", self
self.__del = printer("Base1.__del__")

class Base2:
def __init__(self):
print "Base2.__init__", self
self.__del = printer("Base2.__del__")

class Derived(Base1, Base2):
def __init__(self):
print "Derived.__init__:"
Base1.__init__(self)
Base2.__init__(self)
self.__del = printer("Derived.__del__:")

print "begin..."
d = Derived()
b = d
print "end..."
 
Ad

Advertisements


Top