静态类成员/类引用的生命周期?

时间:2014-09-30 11:01:45

标签: python destructor static-members

asked to show how为一个特殊记录器的旧栗子做单独解决方案。在努力指出不做这种事情的原因时,我仍然尝试过。

这样做,我有一个静态类成员意外消失。

使用此类声明:

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

这些测试文件:

bar.py:

from epiLogger import *

a = epiLogger("bar")

a.info("foo!")
a.info("bar!")
a.info("party!")

test.py:

import bar

我得到了

~ 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$  

但如果我只运行bar.py文件:

~ mgregory$ python bar.py
** My Prologue **
foo!
None
bar!
None
party!
None
destructing bar
first destruction
** My Epilogue **
~ mgregory$ 

似乎一个间接层导致对类本身的引用(访问类变量)变为“无”。

我尝试了一个更简单的测试用例,它不会以这种方式失败(!)

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

导入bar.py时,这不会以同样的方式失败。

我知道这是不尝试此类事情的众多理由之一,但我想了解发生了什么。

1 个答案:

答案 0 :(得分:2)

模块全局变量在Python出口处被清除,并且在__del__挂钩运行时,您的类引用已经消失

不要指望全球仍在那里。相反,使用type(self)来获取类引用:

def __del__(self):
    print "destructing", self.name
    cls = type(self)
    if not cls._finalised.get(self.name):
        print "first destruction"
        self.logger.info('** My Epilogue **')
        cls._finalised[self.name] = True

这在object.__del__ hook documentation警告部分中有记录:

  

此外,当响应于被删除的模块而调用__del__()时(例如,当完成程序的执行时),__del__()方法引用的其他全局变量可能已被删除或在被拆除的过程(例如进口机械关闭)。因此,__del__()方法应该保持维持外部不变量所需的绝对最小值。

考虑到模块全局变量是在字典中维护的,因此清除它们的顺序取决于清除时字典的当前依赖于实现和Python版本的顺序。