Python委托模式 - 如何避免循环引用?

时间:2012-05-16 06:26:41

标签: python delegates circular-reference

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

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

由于我已经搜索了这个问题并且无法找到它的答案,我想知道这是否是Python中的问题,或者是否有一个共同的解决方案(不需要weakref模块)我我不知道?此外,我在询问之前搜索了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用户是否应该担心循环引用:Should I worry about circular references in Python?从他的回答中,我认为即使循环引用的对象最终会被垃圾收集但是将是间接费用。它是否重要取决于该计划。

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

因此,我想补充以下问题以澄清一些尚未解决的问题:

  1. 文件说,通知收集不保证通告 引用的对象。但是从回复中看来并非如此 案件。所以我误解了这段经文还是进一步 我错过了哪些细节?
  2. 我想使用弱引用,如Alex的回复和我的回复中所述 问题,会完全避免问题的开销吗?
  3. 再次感谢回复。

2 个答案:

答案 0 :(得分: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中重载成员访问权限,因此编写方法没有任何好处。

答案 1 :(得分:0)

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

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