5

我想问一下在 Python 中使用委托模式是否会导致循环引用,如果是这样,实现它以确保对象及其委托将被垃圾收集的最佳方法是什么?

在 Objective C 中,通过使用对委托的弱引用来避免上述问题。在 C++ 中,我们不会在委托上调用 delete。我在这里找到了指向 Python 弱引用模块的链接:http: //docs.python.org/library/weakref.html。似乎一个合理的方法可能是创建一个弱引用来使用这个模块来引用实例变量,但我不确定。

由于我用谷歌搜索了这个问题并且无法找到答案,我想知道这是否是 Python 中的一个问题,或者是否有一个我不知道的通用解决方案(不需要弱引用模块)的?此外,我在询问之前确实搜索了 stackoverflow,但我发现的问题要么处理循环导入,要么处理一般的委托模式,而不是特定于 Python 和循环引用的问题。

提前感谢您的任何回复。

下面列出了一些玩具示例的代码,以帮助说明我的问题。我已经以这种方式实现了代码并且它可以工作,但我不确定内存是否最终被垃圾收集。

class A(object):
    def __init__(self):
        self.delegate = None

        # Some other instance variables that keep track of state for performing some tasks.

    def doSomething(self):
        if self.delegate is not None:
            self.delegate.doSomething()
        else:
            print('Cannot perform task because delegate is not set.')

    # Other methods not shown.

class B(object):
    def __init__(self):
        self.a = A() # Need to keep object 'a' from garbage collected so as to preserve its state information.
        self.a.delegate = self  # Is this a circular reference? How to 'fix' it so that A and B will eventually be garbage collected?

    def doSomething(self):
        print('B doing something')

    # Other methods not shown.

编辑

在阅读了一些回复后,我决定澄清我的问题。我了解 Python 具有垃圾收集功能。我不确定它是否会对循环引用的对象执行垃圾收集。我的担忧源于 Python 文档中的以下段落:

CPython 实现细节:CPython 目前使用引用计数方案,对循环链接的垃圾进行(可选)延迟检测,该方案在大多数对象变得无法访问时立即收集它们,但不能保证收集包含循环引用的垃圾。有关控制循环垃圾收集的信息,请参阅 gc 模块的文档。其他实现的行为不同,CPython 可能会改变。当对象变得无法访问时,不要依赖于立即完成对象(例如:始终关闭文件)。

可以在此处找到原始形式的段落:http: //docs.python.org/reference/datamodel.html粗体设置是我的。

以下帖子更清楚地解释了循环引用对象的问题以及为什么它会阻止对这些对象进行垃圾收集(至少在典型设置中):http ://www.electricmonk.nl/log/2008/07/07 /python-destructor-and-garbage-collection-notes/

此外,我刚刚看到 Alex Martellli 对以下问题的回复,即 Python 用户是否应该担心循环引用:我应该担心 Python 中的循环引用吗?从他的回答中,我了解到,即使循环引用的对象最终会被垃圾收集,但也会有开销。是否重要取决于程序。

此外,他提到要使用 Python 的 weakref 模块,但没有明确说明如何使用。

因此,我想添加以下问题以澄清一些未解决的问题:

  1. 文档说循环引用的对象不能保证垃圾收集。但从回复看来,情况并非如此。那么我误解了这篇文章还是我错过了更多细节?
  2. 我想,正如亚历克斯的回复和我的问题中所述,使用弱引用会完全避免问题的开销吗?

再次感谢您的回复。

4

2 回答 2

4

Python 已经进行了垃圾收集。如果您在 C 中编写自己的容器类型作为扩展,您只需要做一些特殊的事情。

演示:运行这个程序,观察内存使用情况不会攀升。

class C(object):
    pass

def circular():
    for x in range(10**4):
        for y in range(10**4):
            a = C()
            b = C()
            a.x = b
            b.x = a

circular()

脚注:下面的函数没有做任何事情,删除它。

def setDelegate(self, delegate):
    self.delegate = delegate

而不是调用x.setDelegate(y),您可以使用x.delegate = y. 您可以在 Python 中重载成员访问,因此编写方法没有任何好处。

于 2012-05-16T06:32:34.677 回答
0

为什么最后不被垃圾收集?当脚本结束并且 python 完成执行时,整个内存部分将被标记为垃圾收集和(最终)操作系统恢复。

如果你在一个长时间运行的程序中运行它,一旦 A 和 B 都被取消引用,那么内存将被回收。

于 2012-05-16T06:37:07.647 回答