This question already has an answer here:
I was asked to show how to do a singleton like solution for the old chestnut of a special logger. At pains to point out the reasons for not doing this sort of thing, still, I tried.
In doing so, I have a static class member disappearing unexpectedly.
With this class declaration:
epiLogger.py:
import logging
class epiLogger():
_initialised = {}
_finalised = {}
def __init__(self, name):
self.logger = logging.getLogger(name)
self.name = name
if not epiLogger._initialised.get(name):
self.logger.addHandler(logging.StreamHandler())
self.logger.setLevel(logging.INFO)
self.logger.info('** My Prologue **')
epiLogger._initialised[self.name] = True
def info(self, the_info):
self.logger.info(the_info)
print epiLogger._finalised.get(self.name)
def __del__(self):
print "destructing", self.name
if not epiLogger._finalised.get(self.name):
print "first destruction"
self.logger.info('** My Epilogue **')
epiLogger._finalised[self.name] = True
And these test files:
bar.py:
from epiLogger import *
a = epiLogger("bar")
a.info("foo!")
a.info("bar!")
a.info("party!")
test.py:
import bar
I get
~ mgregory$ python test.py
** My Prologue **
foo!
None
bar!
None
party!
None
destructing bar
Exception AttributeError: "'NoneType' object has no attribute '_finalised'" in <bound method epiLogger.__del__ of <epiLogger.epiLogger instance at 0x1004a48c0>> ignored
~ mgregory$
But if I run just the bar.py file:
~ mgregory$ python bar.py
** My Prologue **
foo!
None
bar!
None
party!
None
destructing bar
first destruction
** My Epilogue **
~ mgregory$
It seems that one level of indirection has resulted in the reference to the class itself (to access the class variable) has become "None".
I tried a simpler test case, and it does not fail in this way (!)
frob.py:
class frob():
_nasty_global_thingy = True
def __init__(self):
print "initialising a foo", frob._nasty_global_thingy
def __del__(self):
print "destroying a foo", frob._nasty_global_thingy
bar.py:
from frob import *
a = frob()
print a
This does not fail in the same way upon import of bar.py.
I understand that this is one of many reasons not to try this sort of thing, but I would like to understand what is going on, nonetheless.
Module globals are cleaned up on Python exit, and your class reference is already gone by the time the
__del__
hook is run.Don't count on globals still being there. Rather, use
type(self)
to get the class reference:This is documented in the big Warning section on the
object.__del__
hook documentation:Take into account that module globals are maintained in a dictionary, and thus the order in which they are cleared is subject to the current implementation-and-Python-version-dependent order of the dictionary when being cleared.