2009-03-01 39 views
9

Tôi có một dự án mà tôi đang cố gắng sử dụng weakrefs với callbacks, và tôi không hiểu những gì tôi đang làm sai. Tôi đã tạo thử nghiệm đơn giản cho thấy hành vi chính xác mà tôi đang nhầm lẫn với.Tại sao yếu tố không làm việc trên phương pháp bị ràng buộc này?

Tại sao trong thử nghiệm này test_a hoạt động như mong đợi, nhưng yếu cho self.MyCallbackB biến mất giữa khởi tạo lớp và gọi test_b? Tôi nghĩ như miễn là ví dụ (a) tồn tại, tham chiếu đến self.MyCallbackB nên tồn tại, nhưng nó không.

import weakref 

class A(object): 
    def __init__(self): 

     def MyCallbackA(): 
      print 'MyCallbackA' 
     self.MyCallbackA = MyCallbackA 

     self._testA = weakref.proxy(self.MyCallbackA) 
     self._testB = weakref.proxy(self.MyCallbackB) 

    def MyCallbackB(self): 
     print 'MyCallbackB' 

    def test_a(self): 
     self._testA() 

    def test_b(self): 
     self._testB() 

if __name__ == '__main__': 
    a = A()  
    a.test_a() 
    a.test_b() 

Trả lời

10

Bạn muốn có WeakMethod.

Giải thích lý do tại sao giải pháp của bạn không hoạt động có thể được tìm thấy trong các cuộc thảo luận về các công thức:

weakref.refs Thường tới phương pháp ràng buộc hoàn toàn không làm việc theo cách ai hy vọng, bởi vì phương pháp ràng buộc là đối tượng hạng nhất; weakrefs với phương pháp ràng buộc là dead-on-arrival trừ khi một số khác tham chiếu mạnh mẽ đến cùng một phương pháp ràng buộc tồn tại.

4

Theo tài liệu cho các mô-đun Weakref:

Trong phần tiếp theo, các referent hạn có nghĩa là đối tượng mà được gọi bởi một tham chiếu yếu.

Tham chiếu yếu đến một đối tượng không phải là đủ để giữ các đối tượng còn sống: khi tài liệu tham khảo duy nhất còn lại để một vật ám chỉ là tài liệu tham khảo yếu, rác bộ sưu tập là miễn phí để tiêu diệt các referent và tái sử dụng bộ nhớ của nó đối với thứ gì khác.

Whats xảy ra với MyCallbackA là bạn đang nắm giữ một tham chiếu đến nó trong trường hợp của A, nhờ -

self.MyCallbackA = MyCallbackA 

Bây giờ, không có tham chiếu đến phương pháp MyCallbackB ràng buộc trong mã của bạn. Nó chỉ được tổ chức trong một lớp học ___.__ dict__ như một phương pháp không liên kết. Về cơ bản, một phương thức ràng buộc được tạo ra (và trả về cho bạn) khi bạn làm self.methodName. (AFAIK, một phương thức ràng buộc hoạt động như một thuộc tính-sử dụng một bộ mô tả (chỉ đọc): ít nhất là đối với các lớp kiểu mới. Tôi chắc chắn, một cái gì đó tương tự như w/o descriptors sẽ xảy ra cho các lớp kiểu cũ. một người nào đó có kinh nghiệm hơn để xác minh yêu cầu về các lớp kiểu cũ.) Vì vậy, self.MyCallbackB chết ngay khi weakref được tạo ra, bởi vì không có tham chiếu mạnh mẽ đến nó!

kết luận của tôi là dựa trên: -

import weakref 

#Trace is called when the object is deleted! - see weakref docs. 
def trace(x): 
    print "Del MycallbackB" 

class A(object): 
    def __init__(self): 

     def MyCallbackA(): 
      print 'MyCallbackA' 
     self.MyCallbackA = MyCallbackA 
     self._testA = weakref.proxy(self.MyCallbackA) 
     print "Create MyCallbackB" 
     # To fix it, do - 
     # self.MyCallbackB = self.MyCallBackB 
     # The name on the LHS could be anything, even foo! 
     self._testB = weakref.proxy(self.MyCallbackB, trace) 
     print "Done playing with MyCallbackB" 

    def MyCallbackB(self): 
     print 'MyCallbackB' 

    def test_a(self): 
     self._testA() 

    def test_b(self): 
     self._testB() 

if __name__ == '__main__': 
    a = A() 
    #print a.__class__.__dict__["MyCallbackB"] 
    a.test_a() 

Output

Tạo MyCallbackB
Del MycallbackB
Xong chơi với MyCallbackB
MyCallbackA

Lưu ý:
Tôi đã thử xác minh điều này cho các lớp kiểu cũ. Hóa ra là "in a.test_a .__ get__" đầu ra -

<method-wrapper '__get__' of instancemethod object at 0xb7d7ffcc>

cho cả hai lớp phong cách mới và cũ. Vì vậy, nó có thể không thực sự là một descriptor, chỉ cần một cái gì đó mô tả giống như. Trong mọi trường hợp, điểm là một đối tượng phương thức liên kết được tạo ra khi bạn truy cập một phương thức cá thể thông qua bản thân, và trừ khi bạn duy trì một tham chiếu mạnh mẽ đối tượng đó, nó sẽ bị xóa.

3

Các câu trả lời khác giải quyết lý do tại sao trong câu hỏi ban đầu, nhưng không cung cấp cách giải quyết hoặc tham khảo các trang web bên ngoài.

Sau khi làm việc thông qua một số bài đăng khác trên StackExchange về chủ đề này, nhiều trong số đó được đánh dấu là trùng lặp của câu hỏi này, cuối cùng tôi đã đến một giải pháp ngắn gọn. Khi tôi biết bản chất của đối tượng mà tôi đang xử lý, tôi sử dụng mô-đun weakref; khi tôi có thể thay vì phải đối phó với một phương thức bị ràng buộc (như xảy ra trong mã của tôi khi sử dụng các sự kiện gọi lại), bây giờ tôi sử dụng lớp WeakRef sau đây như là một thay thế trực tiếp cho weakref.ref(). Tôi đã thử nghiệm điều này với Python 2.4 thông qua và bao gồm Python 2.7, nhưng không phải trên Python 3.x.

class WeakRef: 

    def __init__ (self, item): 

     try: 
      self.method = weakref.ref (item.im_func) 
      self.instance = weakref.ref (item.im_self) 

     except AttributeError: 
      self.reference = weakref.ref (item) 

     else: 
      self.reference = None 


    def __call__ (self): 

     if self.reference != None: 
      return self.reference() 

     instance = self.instance() 

     if instance == None: 
      return None 

     method = self.method() 

     return getattr (instance, method.__name__) 
+1

Đối với python 3 thay thế 'item.im_func' với' mục .__ func__' và 'item.im_self' với 'mục .__ self__' – lou

+0

Có thể cần' 'def __eq __ (tự, khác): trả khác() == self() ''? Đó có phải là mẫu so sánh tốt nhất không? (và có lẽ '' __ne__'' là nghịch đảo) – Rafe

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