2016-11-22 18 views
5

Tôi đang thực hiện một mô hình quan sát-quan sát được trong python:làm thế nào để thực hiện đúng Observer trong python khi người quan sát là [nên] phá hủy

Đây là lớp học Quan sát:

class Observable(object): 
    def __init__(self, value): 
     self.value = value 
     self.observers = [] 

    def set(self, value): 
     old = self.value 
     self.value = value 
     self.notifyObservers(old, self.value) 

    def get(self): 
     return self.value 

    def addObserver(self, o): 
     self.observers.append(o) 

    def removeObserver(self, o): 
     if o in self.observers: 
      self.observers.remove(o) 

    def notifyObservers(self, old, new): 
     for o in self.observers: 
      o.valueChanged(old, new) 

và điều này là người quan sát:

class Observer(object): 
    def __init__(self, foo): 
     self.foo = foo 
     self.foo.addObserver(self) 

    def __del__(self): 
     print('Observer.__del__ called') 
     self.foo.removeObserver(self) 

    def valueChanged(self, old, new): 
     print('foo changed from %s to %s' % (old, new)) 

Mã hoạt động như mong đợi.

Nhưng tôi cần Observer bị hủy (tức là khi nó không được đề cập đến, nó sẽ tự xóa chính nó khỏi danh sách các nhà quan sát trong đối tượng Observable).

Vấn đề là với mã này, Observer.__del__ không bao giờ được gọi nếu số Observer nằm trong danh sách các nhà quan sát của một số đối tượng Observable.

Lưu ý rằng tôi không nhất thiết phải tiêu diệt các Observer một cách rõ ràng, nó cũng sẽ đi unreferenced vì giao biến, do đó gọi removeObserver() một cách rõ ràng trước khi phá hủy là không khả thi.

Nếu tôi nhận xét ra self.foo.addObserver(self), thì không có tham chiếu bổ sung nào cho Observer và gọi del trên đó sẽ gọi Observer.__del__.

Các testcase cho kịch bản này là:

foo = Observable(23) 
bar = Observer(foo) 
foo.set(44) 
bar = None 
foo.set(1) 

nó có hai kết quả:

  • nếu self.foo.addObserver(self) không được nhận xét ra, nó in foo changed from 23 to 44foo changed from 44 to 1
  • nếu self.foo.addObserver(self) là nhận xét ra, nó in Observer.__del__ called
+2

Bạn đã xem xét yếu kém? Tham chiếu yếu được thiết kế để giải quyết chính xác vấn đề này và đã là một tính năng của Python kể từ 2.4. –

Trả lời

4

Dường như tham chiếu yếu sẽ giải quyết vấn đề của bạn. Bạn thay đổi các quan sát viên để quản lý weak-references, ví dụ: bằng cách thay thế list trong một weakref.WeakKeyDictionary hoặc bằng cách triển khai một số vùng chứa tham chiếu yếu khác. BTW, sử dụng một loại băm, chẳng hạn như một từ điển, cũng sẽ tốt hơn một danh sách kể từ khi loại bỏ một người quan sát sẽ hiệu quả hơn nhiều.

0

Giải pháp: (thay đổi Observable.observers để weakref.WeakKeyDictionary)

class Observable(object): 
    def __init__(self, value): 
     self.value = value 
     self.observers = weakref.WeakKeyDictionary() 

    def set(self, value): 
     old = self.value 
     self.value = value 
     self.notifyObservers(old, self.value) 

    def get(self): 
     return self.value 

    def addObserver(self, o): 
     self.observers[o] = 1 

    def removeObserver(self, o): 
     del self.observers[o] 

    def notifyObservers(self, old, new): 
     for o in self.observers: 
      o.valueChanged(old, new) 

Ngoài ra, nó không phải là cần thiết để gọi .removeObserver(self) trong destructor của người quan sát.

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