2009-10-02 54 views
42

適當地使用weakref我有一些代碼,其中的類的實例有家長< - >子引用對方,如:如何以及何時在Python

class Node(object): 
    def __init__(self): 
    self.parent = None 
    self.children = {} 
    def AddChild(self, name, child): 
    child.parent = self 
    self.children[name] = child 

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
Run() 

認爲這將創建循環引用,使得在Run()完成後,root,c1c2不會被釋放,對嗎?那麼,如何讓他們獲得釋放?我想我可以做點像root.children.clear()self.parent = None - 但是如果我不知道什麼時候該做什麼?

這是一個適當的時間來使用weakref模塊?什麼,確切地說,我弱化refrefify? parent屬性? children屬性?整個對象?上述所有的?我看到關於WeakKeyDictionary和weakref.proxy的討論,但我不清楚在這種情況下應該如何使用它們。

這也是python2.4(不能升級)。

更新:互相依賴的例子和總結

什麼反對weakref-IFY取決於哪個對象生活中可以沒有其他的,什麼對象。壽命最長的物體應該包含較短壽命物體的弱點。類似地,不應該對依賴關係進行弱引發 - 如果是這樣,依賴關係可能會默默地消失,即使它仍然是需要的。

如果,例如,你有一個樹狀結構,root,有孩子,kids,但可以存在沒有孩子,那麼root對象應使用weakrefs其kids。如果子對象取決於父對象的存在,情況也是如此。在下面,子對象要求父母爲來計算其深度,因此爲parent的強引用。儘管kids屬性的成員是可選的,但是使用weakrefs來防止循環引用。

class Node: 
    def __init__(self) 
    self.parent = None 
    self.kids = weakref.WeakValueDictionary() 
    def GetDepth(self): 
    root, depth = self, 0 
    while root: 
     depth += 1 
     root = root.parent 
    return depth 
root = Node() 
root.kids["one"] = Node() 
root.kids["two"] = Node() 
# do what you will with root or sub-trees of it. 

要翻轉關係,我們有如下所示。在這裏,Facade類需要一個Subsystem實例才能工作,所以他們使用強引用到他們需要的子系統。然而,Subsystem不需要Facade工作。 Subsystem只是提供一種方式來通知Facade關於彼此的行爲。

class Facade: 
    def __init__(self, subsystem) 
    self.subsystem = subsystem 
    subsystem.Register(self) 

class Subsystem: 
    def __init__(self): 
    self.notify = [] 
    def Register(self, who): 
    self.notify.append(weakref.proxy(who)) 

sub = Subsystem() 
f1 = CliFacade(sub) 
f2 = WebFacade(sub) 
# Go on to reading from POST, stdin, etc 

回答

25

是的,weakref在這裏很棒。具體來說,而不是:

self.children = {} 

使用:

self.children = weakref.WeakValueDictionary() 

沒有別的需要,更改您的代碼。這樣,當一個孩子沒有其他差異時,它就會消失 - 父母的children地圖中的條目也會以該孩子爲值。

避免參考循環與實現緩存一樣高,以此作爲使用weakref模塊的動機。參考循環不會殺死你,但它們可能最終會堵塞你的記憶,尤其是你的記憶。如果其中涉及實例的某些類定義爲__del__,則會干擾gc的模塊解散這些循環的能力。

+0

另外,如果您確定不需要循環gc,則可以禁用它以實現小的性能提升。 – 2009-10-02 05:11:50

+1

謝謝,亞歷克斯。是否有一個特定的原因來弱報告「children」而不是'parent'?效果會一樣嗎?如果'父母'也是弱refref會發生什麼?在雙鏈表的情況下,「prev」,「next」還是兩者都應該是weakrefs? – 2009-10-02 08:08:08

+5

這是不好的建議。示例中的所有子節點將在從Run()返回後立即被破壞。一般來說,你幾乎總是把一個結構體的根綁定到變量上,所以正確的方法是將'weakref'用於'parent',而不是'children'。 – 2009-10-02 08:10:04

13

我建議使用child.parent = weakref.proxy(self)。在(外部參照)parent的使用期限涵蓋child的使用期限的情況下,這是避免循環參考的好辦法。相反,當child的使用期限涵蓋parent的使用期限時,使用weakref代替child(如Alex建議)。但是,如果parentchild都不存在,則從不使用weakref

這裏用示例說明這些規則。使用weakref-ED父母,如果你存儲的根在一些變量,並通過它,而孩子們從中訪問:如果他們都綁定到變量

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
    return root # Note that only root refers to c1 and c2 after return, 
       # so this references should be strong 

使用weakref-ED的孩子,而根則通過他們訪問:

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
    return c1, c2 

但也不會爲以下工作:

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
    return c1 
+2

有很多情況下,只要其他實體仍然持有對他們的引用,孩子和父母中的任何一方或兩方都可以在沒有其他實體的情況下活着。那就是當你可能想要使用相互弱的引用時(那些其他的外部引用將會根據需要完成保持實體活動的工作)。 – 2009-10-02 14:44:11

+2

**「我建議使用'child.parent = weakref.proxy(self)'。」** _Thisss._這是一個常見情況的典型方法,其中一個長壽命的「父」包含多個短命的孩子'-ren。亞歷克西斯的解決方案更適用於擁有「一個短暫的」父母「的多重長壽'子女'的邊緣案例,這在我看來是很少見的。 – 2016-01-29 06:39:07

1

我想澄清的引用可能很弱。以下方法是通用的,但是我在所有示例中使用了雙鏈樹。

合乎邏輯的步驟1.

你需要確保有很強的參考,以保持所有對象,只要活着,你需要他們。它可以在許多方面的工作要做,例如:

  • [直接名稱]:命名的參照每個節點樹
  • [容器]:到容器的引用存儲所有節點
  • 從每個節點到其子
  • [葉+父]到根節點的引用,和參考文獻:
  • [根+兒童]到所有葉節點的引用,以及從每個節點到其父
引用

邏輯步驟2.

如果需要,現在您添加引用來表示信息。例如,如果您在步驟1中使用[容器]方法,則仍然需要表示邊緣。節點A和B之間的邊緣可以用單個參考來表示;它可以沿任何方向前進。同樣,也有多種選擇,例如:

  • [兒童]:從每個節點到其子引用
  • [父]:從每個節點到它的父參考
  • [集合的集合] :包含2元素集合的集合;每個2元素包含引用一個邊緣

當然的節點,如果使用[根+兒童]在步驟1中的方法,所有的信息已經充分表示,因此跳過這一步。

邏輯步驟3.

現在您添加引用以提高性能,如果需要的話。例如,如果您在步驟1中使用[容器]方法,而在步驟2中使用[子項]方法,則可能希望提高某些算法的速度,並在每個節點與其父項之間添加引用。這些信息在邏輯上是多餘的,因爲您可以(以性能爲代價)從現有數據中獲取它。


所有步驟1中的引用必須要堅強

步驟2和3中的所有參考文獻可能較弱或較強。使用強引用沒有優勢。使用弱引用有一個好處,直到你知道週期不再可能。嚴格地說,一旦你知道週期是不可能的,那麼使用弱還是強參考都沒有區別。但是爲了避免考慮它,你也可以在步驟2和步驟3中只使用弱引用。

相關問題