Запись в Python: почему __init__ вызывается дважды?
Я пытаюсь использовать ведение журнала python с конфигурационным файлом и собственным обработчиком. Это работает в некоторой степени. Меня действительно озадачивает __init__
, вызываемый дважды, и __del__
вызывается один раз. Когда я удаляю весь файл конфигурационного файла и создаю обработчик непосредственно внутри кода __init__
, вызывается один раз, а __del__
никогда не вызывается.
Мои вопросы:
- Почему
__init__
вызывается дважды?
- Почему
__del__
называется менее часто, чем __init__
?
Код:
#!/bin/env python
import logging
import logging.handlers
import logging.config
class Test1TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def __init__(self,filename):
print "init called"
logging.handlers.TimedRotatingFileHandler.__init__(self,filename, when='S', interval=86400, backupCount=8, encoding=None)
def __del__(self):
print "del called"
if hasattr(logging.handlers.TimedRotatingFileHandler,"__del__"):
logging.handlers.TimedRotatingFileHandler.__del__(self)
logging.config.fileConfig('/root/test1.conf')
logger = logging.getLogger("test1")
Файл конфигурации:
[formatters]
keys: simple
[handlers]
keys: file
[loggers]
keys: root
[formatter_simple]
format: "%(message)s"
[handler_file]
class: test1.Test1TimedRotatingFileHandler
args: ("/root/test1.log",)
level=INFO
[logger_root]
level: INFO
handlers: file
qualname: test1
Результат выглядит следующим образом:
init called
init called
del called
Использование отладчика для получения трассировки стека, как предложено Sentinal, показывает следующее:
Первый вызов:
> /root/test1.py(12)__init__()
-> print "init called"
(Pdb) where
/root/test1.py(21)<module>()
-> logging.config.fileConfig('/root/test1.conf')
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig()
-> handlers = _install_handlers(cp, formatters)
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(156)_install_handlers()
-> klass = _resolve(klass)
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(94)_resolve()
-> found = __import__(used)
/root/test1.py(21)<module>()
-> logging.config.fileConfig('/root/test1.conf')
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig()
-> handlers = _install_handlers(cp, formatters)
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(159)_install_handlers()
-> h = klass(*args)
> /root/test1.py(12)__init__()
-> print "init called"
(Pdb) c
init called
Второй вызов:
> /root/test1.py(12)__init__()
-> print "init called"
(Pdb) w
/root/test1.py(21)<module>()
-> logging.config.fileConfig('/root/test1.conf')
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(84)fileConfig()
-> handlers = _install_handlers(cp, formatters)
/usr/local/python/2.6.4/lib/python2.6/logging/config.py(159)_install_handlers()
-> h = klass(*args)
> /root/test1.py(12)__init__()
-> print "init called"
Ответы
Ответ 1
- Почему дважды повторяется init?
Если вы следуете коду модуля logging
, вы увидите, что при загрузке файла конфигурации ведения журнала он запускает все обработчики (первая инстанция).
В коде вы объявляете своего обработчика как test1.Test1TimedRotatingFileHandler
, поэтому, когда он пытается импортировать обработчик, он анализирует код в модуле test1... поэтому он воссоздает обработчик!
Исправленный код будет защищен с помощью __name__ == '__main__'
:
#!/bin/env python
import logging
import logging.handlers
import logging.config
class Test1TimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def __init__(self,filename):
print "init called"
logging.handlers.TimedRotatingFileHandler.__init__(self,filename, when='S', interval=86400, backupCount=8, encoding=None)
def __del__(self):
print "del called"
if hasattr(logging.handlers.TimedRotatingFileHandler,"__del__"):
logging.handlers.TimedRotatingFileHandler.__del__(self)
if __name__ == "__main__":
logging.config.fileConfig('./test1.conf')
logger = logging.getLogger("test1")
2. Почему del называется менее, чем init?
В общем случае оператор __del__
вызывается, когда -python-хочет, точнее, он вызывается, когда сборщик мусора решает собирать мусор; это не обязательно сразу после его выпуска.
Ответ 2
Вам не хватает защитного кода if __name__ == "__main__":
для вашего кода конфигурации ведения журнала. Выполняется второй раз, когда logging
импортирует ваш модуль test1
, чтобы найти ссылку на класс.
В качестве альтернативы, используйте имя __main__.Test1TimedRotatingFileHandler
в файле конфигурации, иначе введите код конфигурации и класс обработчика в разные файлы.