2009-10-02 39 views
42

Tôi có một số mã nơi thể hiện của lớp có mẹ < -> con tham khảo với nhau, ví dụ:Làm thế nào và khi nào thì thích hợp sử dụng weakref bằng 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() 

tôi nghĩ điều này tạo ra tham chiếu vòng tròn như vậy mà root, c1c2 sẽ không được giải phóng sau khi Run() được hoàn tất, đúng không ?. Vậy làm cách nào để họ được tự do? Tôi nghĩ tôi có thể làm điều gì đó như root.children.clear() hoặc self.parent = None - nhưng nếu tôi không biết khi nào thì làm như vậy?

Đây có phải là thời điểm thích hợp để sử dụng mô-đun weakref không? Cái gì, chính xác, tôi có suy yếu không? thuộc tính parent? Thuộc tính children? Toàn bộ vật thể? Tất cả những điều trên? Tôi thấy nói về WeakKeyDictionary và weakref.proxy, nhưng nó không rõ ràng với tôi làm thế nào họ nên được sử dụng, nếu ở tất cả, trong trường hợp này.

Đây cũng là trên python2.4 (không thể nâng cấp).

Cập nhật: Ví dụ và Tóm tắt

gì phản đối weakref-IFY phụ thuộc vào đối tượng có thể sống mà không có người kia, và những gì đối tượng phụ thuộc vào nhau. Đối tượng tồn tại lâu nhất nên chứa weakrefs đối với các đối tượng sống ngắn hơn. Tương tự như vậy, weakrefs không nên được thực hiện cho các phụ thuộc - nếu chúng là, phụ thuộc có thể âm thầm biến mất mặc dù nó vẫn còn cần thiết.

Nếu, ví dụ, bạn có một cấu trúc cây, root, có trẻ em, kids, nhưng có thể tồn tại mà không trẻ em, sau đó đối tượng root nên sử dụng weakrefs cho kids của nó. Đây cũng là trường hợp nếu đối tượng con phụ thuộc vào sự tồn tại của đối tượng cha. Bên dưới, đối tượng con yêu cầu một phụ huynh để tính toán độ sâu của nó, do đó mạnh mẽ cho số parent. Tuy nhiên, các thành viên của thuộc tính kids là tùy chọn, do đó, weakrefs được sử dụng để ngăn chặn tham chiếu vòng tròn.

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. 

Để lật mối quan hệ xung quanh, chúng tôi có nội dung như bên dưới. Ở đây, các lớp học Facade yêu cầu một cá thể Subsystem để hoạt động, do đó, chúng sử dụng một tham chiếu mạnh mẽ cho hệ thống con mà họ cần. Tuy nhiên, Subsystem s không yêu cầu Facade hoạt động. Subsystem s chỉ cung cấp một cách để thông báo Facade về các hành động của nhau.

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 

Trả lời

25

Yep, weakref xuất sắc tại đây. Cụ thể, thay vì:

self.children = {} 

sử dụng: nhu cầu

self.children = weakref.WeakValueDictionary() 

Không có gì khác thay đổi trong mã của bạn. Bằng cách này, khi một đứa trẻ không có sự khác biệt nào khác, nó chỉ biến mất - và mục nhập trong bản đồ children của cha mẹ có con đó làm giá trị.

Tránh vòng tham chiếu tăng cao ngang bằng với việc triển khai bộ đệm làm động lực để sử dụng mô-đun weakref. Vòng lặp sẽ không giết bạn, nhưng chúng có thể làm tắc nghẽn bộ nhớ của bạn, đặc biệt là.nếu một số lớp có các cá thể liên quan đến chúng, hãy định nghĩa __del__, vì nó cản trở khả năng của mô-đun của gc để giải tán các vòng lặp đó.

+0

Ngoài ra, nếu bạn chắc chắn bạn sẽ không cần đến gc tuần hoàn, bạn có thể tắt nó để tăng hiệu suất nhỏ . –

+1

Cảm ơn, Alex. Có lý do cụ thể nào để suy yếu 'trẻ em' thay vì' cha mẹ'? Liệu hiệu ứng có giống nhau không? Điều gì sẽ xảy ra nếu 'cha mẹ' cũng yếu ớt? Trong trường hợp của một danh sách liên kết kép, nên 'prev',' next', hoặc cả hai đều là weakrefs? –

+5

Đây là gợi ý không tốt. Tất cả trẻ em trong ví dụ này sẽ bị hủy ngay sau khi trở về từ 'Run()'. Nói chung, bạn hầu như luôn luôn ràng buộc một gốc của cấu trúc thành biến, do đó, đúng cách là sử dụng 'weakref' cho' parent', nhưng không phải là 'children'. –

13

Tôi khuyên bạn nên sử dụng child.parent = weakref.proxy(self). Đây là giải pháp tốt để tránh các tham chiếu vòng tròn trong trường hợp thời gian tồn tại của (tham chiếu bên ngoài) parent bao gồm tuổi thọ của child. Ngược lại, sử dụng weakref cho child (như Alex đề xuất) khi tuổi thọ của child bao gồm toàn bộ thời gian của parent. Nhưng không bao giờ sử dụng weakref khi cả hai parentchild đều có thể hoạt động mà không có sự khác.

Ở đây, các quy tắc này được minh họa bằng các ví dụ. Sử dụng weakref-ed mẹ nếu bạn lưu trữ gốc trong một số thay đổi và vượt qua nó xung quanh, trong khi trẻ em được truy cập từ nó:

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 

Sử dụng trẻ em weakref-ed nếu bạn ràng buộc tất cả chúng để biến, trong khi gốc được truy cập thông qua họ :

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

Nhưng không phải sẽ làm việc sau:

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

Có rất nhiều trường hợp trong đó một hoặc cả hai con và cha mẹ có thể được sống mà không có các _as khác như các thực thể khác chưa giữ tài liệu tham khảo cho họ_; đó là khi bạn có thể muốn sử dụng các tham chiếu lẫn nhau yếu (các tham chiếu bên ngoài khác sẽ thực hiện công việc giữ các thực thể sống chính xác chừng nào cần thiết). –

+2

** "Tôi đề nghị sử dụng' child.parent = weakref.proxy (self) '." ** _Thisss._ Đây là cách tiếp cận kinh điển cho trường hợp phổ biến của một "cha mẹ" tồn tại lâu dài có chứa nhiều 'con sống ngắn ' '-ren. Giải pháp của Alexis được áp dụng nhiều hơn cho trường hợp cạnh của nhiều 'con'-ren 'tồn tại lâu dài' sở hữu 'một" cha mẹ ngắn ngủi "mà tôi hiếm khi thấy trong tự nhiên. –

1

tôi muốn làm rõ mà tài liệu tham khảo có thể bị yếu. Cách tiếp cận sau đây là chung, nhưng tôi sử dụng cây được liên kết kép trong tất cả các ví dụ.

Bước hợp lý 1.

Bạn cần đảm bảo có tham chiếu mạnh mẽ để giữ cho tất cả các đối tượng còn sống miễn là bạn cần chúng. Nó có thể được thực hiện bằng nhiều cách, ví dụ bằng cách:

  • [tên trực tiếp]: một tài liệu tham khảo đặt tên cho mỗi node trong cây
  • [thùng]: một tham chiếu đến một container lưu trữ tất cả các nút
  • [root + trẻ em]: một tham chiếu đến nút gốc, và tài liệu tham khảo từ mỗi nút để con của nó
  • [lá + mẹ]: tham chiếu đến tất cả các nút lá, và tài liệu tham khảo từ mỗi nút để mẹ

Bước logic 2.

Bây giờ bạn thêm tài liệu tham khảo để đại diện cho thông tin, nếu được yêu cầu.

Ví dụ: nếu bạn đã sử dụng cách tiếp cận [container] ở Bước 1, bạn vẫn phải thể hiện các cạnh. Một cạnh giữa các nút A và B có thể được biểu diễn bằng một tham chiếu đơn; nó có thể đi theo một trong hai hướng. Một lần nữa, có rất nhiều lựa chọn, ví dụ:

  • [con]: tài liệu tham khảo từ mỗi nút để con của nó
  • [cha]: một tài liệu tham khảo từ mỗi nút để mẹ
  • [tập các bộ] : tập hợp chứa 2 phần tử; mỗi phần tử 2 chứa tham chiếu đến các nút của một cạnh

Tất nhiên thông tin của bạn đã được đại diện đầy đủ, vì vậy bạn bỏ qua bước này.

Bước logic 3.

Bây giờ bạn thêm tham chiếu để cải thiện hiệu suất, nếu muốn. Ví dụ: nếu bạn đã sử dụng cách tiếp cận [container] ở Bước 1 và [con] ở Bước 2, bạn có thể muốn cải thiện tốc độ của các thuật toán nhất định và thêm tham chiếu giữa mỗi nút và cha mẹ của nút đó. Thông tin như vậy là dự phòng hợp lý, vì bạn có thể (với chi phí hoạt động) lấy được từ dữ liệu hiện có.


Tất cả các tham chiếu trong Bước 1 phải mạnh.

Tất cả các tham chiếu trong bước 2 và 3 có thể yếu hoặc mạnh. Không có lợi thế khi sử dụng các tham chiếu mạnh mẽ. Có một lợi thế khi sử dụng các tham chiếu yếu cho đến khi bạn biết rằng các chu trình không còn khả thi nữa. Nói đúng ra, một khi bạn biết rằng chu trình là không thể, nó không tạo ra sự khác biệt nào nếu sử dụng các tham chiếu yếu hoặc mạnh. Nhưng để tránh suy nghĩ về điều đó, bạn cũng có thể sử dụng các tham chiếu yếu trong các bước 2 và 3.

Các vấn đề liên quan