什么时候收集python类和类属性垃圾?

时间:2015-02-15 06:28:20

标签: python class garbage-collection class-attributes class-attribute

class Member(object):
    def __init__(self, identifier):
        self.identifier = identifier
        print "Member __init__", self.identifier

    def __del__(self):
        print "Member __del__", self.identifier
        with open("/home/might/" + self.identifier, "w") as outF:
            outF.write(self.identifier)

class WithMembers(object):
    def __init__(self):
        print "WithMembers __init__"
        print WithMembers.classMem
        self.instanceMem = Member("instance mem")

    def __del__(self):
        print "WithMembers __del__"

    classMem = Member("class mem")

if __name__ == "__main__":
    print "main"
    WithMembers()
    #del WithMembers.classMem       # "Member __del__ class mem" before "end"
    #del WithMembers                # "Member __del__ class mem" after "end"
    print "end"

以上代码位于Hidden.py中,运行python Hidden.py会产生以下输出:

Member __init__ class mem
main
WithMembers __init__
<__main__.Member object at 0x935aeec>
Member __init__ instance mem
WithMembers __del__
Member __del__ instance mem
end

我没有在输出或Member __del__ class mem文件中看到class mem,除非我取消评论其中一个del语句。为什么是这样?什么时候收集python类和类属性?

2 个答案:

答案 0 :(得分:2)

这被报告为http://bugs.python.org/issue1545463中的一个错误,修复了3.4但未向后移植(我正在运行2.7)。这也在http://code.activestate.com/lists/python-list/504216/中解释过。请参阅下面的python 3.5中的输出。

基于以上所述,我的理解是在2.7中,当解释器退出时,新的样式类WithMembers仍然存在(不会被GC清理)。因此,classMem不会被垃圾回收,因为WithMembers仍然引用它。

请注意,新样式类具有来自__mro__和一些内置描述符(http://bugs.python.org/issue17950)的循环引用。尽管模块清理后模块级的新式类被GC认为是死的,但是在模块清理后GC调用它们会被禁用,因为它会导致太多其他问题。

这不会导致内存泄漏,因为操作系统会在解释器退出后清理资源。

class Member(object):
    def __init__(self, identifier):
        self.identifier = identifier
        print("Member __init__ " + self.identifier)

    def __del__(self):
        print("Member __del__ " + self.identifier)
        with open("/home/might/" + self.identifier, "w") as outF:
            outF.write(self.identifier)

class WithMembers(object):
    def __init__(self):
        print("WithMembers __init__")
        print(WithMembers.classMem)
        self.instanceMem = Member("instance mem")

    def __del__(self):
        print("WithMembers __del__")

    classMem = Member("class mem")

if __name__ == "__main__":
    print("main")
    WithMembers()
    print("end")
使用python3 Hidden.py

运行时,

输出以下内容

Member __init__ class mem
main
WithMembers __init__
<__main__.Member object at 0xb6fc8e2c>
Member __init__ instance mem
WithMembers __del__
Member __del__ instance mem
end
Member __del__ class mem
Exception ignored in: <bound method Member.__del__ of <__main__.Member object at 0xb6fc8e2c>>
Traceback (most recent call last):
  File "class_member_gc.py", line 8, in __del__
NameError: name 'open' is not defined

答案 1 :(得分:0)

classMem是类WithMembers的类变量,这意味着它将由此类的所有实例共享。它在Python中是一个全球性的东西。 这就是为什么班级成员__del__在退出该计划时没有被调用的原因。

这里有一个问题:为什么Python在退出程序时不会简单地将所有引用计数设置为0,以便可以调用所有__del__函数?

与C ++一样,它保证调用全局变量的析构函数。 Python中保证这一点的唯一方法是运行所有模块并删除所有变量。但这意味着__del__方法不能相信它可能想要使用的任何全局变量仍然存在,因为无法知道变量的删除顺序。

有两种方法可以强制销毁classMem

一个是del WithMembers.classMem。这将减少对0的引用计数,并自动调用__del__

另一个是使类WithMembers成为旧式类(不是从object继承)。像这样:

...

class WithMembers:
    def __init__(self):
        print "WithMembers __init__"
        print WithMembers.classMem
        self.instanceMem = Member("instance mem")
...        

输出将是:

Member __init__ class mem
main
WithMembers __init__
<__main__.Member object at 0x00000000026C5278>
Member __init__ instance mem
WithMembers __del__
Member __del__ instance mem
end
Member __del__ class mem

这是一个非常有用的链接,可帮助您更好地理解此答案。 http://www.electricmonk.nl/log/2008/07/07/python-destructor-and-garbage-collection-notes/

希望它有所帮助。 :)