【问题标题】:When are python classes and class attributes garbage collected?python类和类属性什么时候被垃圾收集?
【发布时间】:2015-04-15 21:53:18
【问题描述】:
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

除非我取消注释del 语句之一,否则我在输出或class mem 文件中看不到Member __del__ class mem。为什么是这样? python类和类属性什么时候被垃圾回收?

【问题讨论】:

  • 您的代码与垃圾收集没有任何关系。 del &lt;identifier&gt; 取消引用该标识符,使其不再指向现有对象。当这种情况发生时,your_object.__del__ 会被调用。一段时间后,在该对象没有任何引用后,它就会被垃圾回收。
  • 可能永远不会,这是有据可查的。考虑使用上下文管理器。
  • @cdarke:确实有据可查,而且可能永远不会依赖于 python 版本。我做了一些研究并发布了stackoverflow.com/a/28552889/4455221

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


【解决方案1】:

这被报告为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

【讨论】:

    【解决方案2】:

    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/

    希望对您有所帮助。 :)

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-07
    • 1970-01-01
    • 2020-11-29
    • 2015-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多