我试图在
python中实现一个非常简单的
observer pattern.
这是我的Observer类(它实际上只是一个接口,我想我实际上并不需要它):
class Observer():
def update(self,subject,message): pass
和我的主题类(又名Observable,但我更喜欢主题):
class Subject():
def __init__(self):
self.observers = []
def registerObserver(self, observer):
if observer not in self.observers:
self.observers.append(observer)
def removeObserver(self, observer):
self.observers.remove(observer)
def notifyObservers(self, message = None):
for observer in self.observers:
observer.update(self,message)
A类包含嵌套的DelNotifier类,它是Subject的子类.当删除A类对象(实际上是垃圾收集,因为它在__del__方法中)时,A.DelNotifier将通知其所有观察者删除.
class A():
def __init__(self, name):
self.name = name
self.delNotifier = A.DelNotifier(self)
class DelNotifier(Subject):
def __init__(self, outer):
super(A.DelNotifier,self).__init__()
self.outer = outer
def notifyObservers(self):
Subject.notifyObservers(self,"This is Class A object " + self.outer.name + ": I'm dying!")
def registerB(self,observer):
if not isinstance(observer,B): raise ValueError("Can only register Class B objects with Class A.")
self.delNotifier.registerObserver(observer.Aobserver)
def deleteme(self):
print("Must notify observers of my impending doom first...")
self.delNotifier.notifyObservers()
def __str__(self):
return "Class A object " + self.name
def __del__(self):
self.deleteme()
print("Done notifying everyone, time to go gentle into that good night.")
B类包含一个嵌套的AObserver类,它是Observer的子类,当A被删除时,它将从类A.DelNotifier主题接收消息(实际上,当A对象被垃圾收集时会发生这种情况):
class B():
def __init__(self, name, a):
self.name = name
self.Aobserver = B.AObserver(self)
a.registerB(self)
class AObserver(Observer):
def __init__(self,outer):
super(B.AObserver,self).__init__()
self.outer = outer
def update(self,subject,message):
print(str(self.outer) + " received message: '" + str(message) + "'")
print("Time for", self.outer, "to die, too.")
self.outer.__del__()
def __str__(self):
return "Class B object " + self.name
def __del__(self):
print("This is " + str(self) + ": now I'm dying, too!")
当我直接调用__del __()时,这种设计有效,但是,当会话退出时,某些对象似乎是第二次gc’d:
>>> a = A('a')
>>> b1 = B('b1', a)
>>> b2 = B('b2', a)
>>> a.__del__()
Must notify observers of my impending doom first...
Class B object b1 received message: 'This is Class A object a: I'm dying!'
Time for Class B object b1 to die, too.
This is Class B object b1: now I'm dying, too!
Class B object b2 received message: 'This is Class A object a: I'm dying!'
Time for Class B object b2 to die, too.
This is Class B object b2: now I'm dying, too!
Done notifying everyone, time to go gentle into that good night.
>>> exit()
Must notify observers of my impending doom first...
Class B object b1 received message: 'This is Class A object a: I'm dying!'
Time for Class B object b1 to die, too.
This is Class B object b1: now I'm dying, too!
Class B object b2 received message: 'This is Class A object a: I'm dying!'
Time for Class B object b2 to die, too.
This is Class B object b2: now I'm dying, too!
Done notifying everyone, time to go gentle into that good night.
This is Class B object b1: now I'm dying, too!
This is Class B object b2: now I'm dying, too!
另一个问题,我认为这更重要的是,当我从列表中删除A类项目时,该项目不会立即被垃圾收集,我无法确定是否已删除任何已注册的B项目:
>>> b1 = B('b1',a[0])
>>> b2 = B('b2',a[0])
>>> del a[0]
## Note that items are not deleted until session exits
>>> exit()
Must notify observers of my impending doom first...
Class B object b1 received message: 'This is Class A object a: I'm dying!'
Time for Class B object b1 to die, too.
This is Class B object b1: now I'm dying, too!
Class B object b2 received message: 'This is Class A object a: I'm dying!'
Time for Class B object b2 to die, too.
This is Class B object b2: now I'm dying, too!
Done notifying everyone, time to go gentle into that good night.
##Note that the class B objects get gc'd a second time....???
This is Class B object b1: now I'm dying, too!
This is Class B object b2: now I'm dying, too!
除了这些问题之外,我知道依赖于__del__方法除了在gc’d之后清理一个对象以外的任何事情所固有的许多问题,并且为了我的目的可能应该避免它试图雇用.但我不知道另一种方式.
什么是更好的方法来做到这一点?我已经考虑过在使用它们之后尝试使用上下文管理器(with)来删除它们,但我没有这方面的经验.如果这是一个不错的选择,我将如何做到这一点?它会是什么样子?
编辑:澄清所需的行为
我将试图澄清一些(可理解的)混淆.
我已经将代码简化了一点,但B是依赖于对象A的对象.如果B的A消失,那么B应该消失.我将有一些容器(使用此处的列表)的As和Bs:
As = [A('a'+str(i)) for i in range(10)]
Bs = [B('b'+str(i),As[i]) for i in range(10)] #Bs made of As
del As[0] #whoops, don't need As[0] anymore
assert Bs[0] is None #ERROR!
#or using pop:
As.pop(0)
assert Bs[0] is None #ERROR!
see my previous question from the other day也帮助我了解了首先使用观察者模式的想法.
最佳答案 这是一个很大的代码差异,以满足您自动维护引用列表的要求,以及删除引用时的清理.我添加了一个Manager类来实现这一点,并且第二次传递了deleted()事件,它是管理器清理它正在维护的列表的一个钩子.我在这里重新发布了完整的修改后的代码,因为更新我以前的答案并非易事.
我相信这完全满足了你提出的问题.也许不是你首先需要这个的原因,但我想你在另一个问题上问过这个问题.
我们需要弱引用才能使其工作:
import weakref
Observer接口获取一个在deleted()后调用的新方法
class Observer(object):
def update(self,subject,message): pass
def deleting(self,subject):
''' the subject is being deleted '''
pass
def deleted(self,subject):
pass
经理类维护主题和观察者列表
class Manager(Observer):
def __init__(self, ):
self._subjects = []
self._observers = []
def monitorSubject(self, subject):
self._subjects.append( weakref.ref(subject) )
# observe the subject for deletion to
# trigger list maintenance on "deleted"
subject.registerObserver(self)
def monitorObserver(self, observer):
self._observers.append( weakref.ref(observer) )
def deleted(self, subject):
''' a subject was deleted, remove it from the list.
deleting() is called first, and the observers delete themselves.
deleted() is called next, and is a hook for the manager to
cleanup any dead subjects and observers '''
# calling a weakref returns the original object, and `None` when the
# reference is dead
def isNotDead(r):
return not r()==None
# remove any dead subjects
print 'Removing dead subjects...'
self._subjects = filter(isNotDead, self._subjects)
# remove any dead observers
print 'Removing dead observers...'
self._observers = filter(isNotDead, self._observers, )
def __str__(self,):
return "[Manager] Subjects:{0}, Observers:{1}".format(
','.join([str(r()) for r in self._subjects]),
','.join([str(r()) for r in self._observers])
)
评论中注明了主题的差异,但主要是第二次调用已删除.删除的第一个通知通知观察者,而删除的第二个通知通知管理员.此外,__ del__例程使用弱refs迭代,因为其中一些被删除.
class Subject(object):
def __init__(self):
self.observers = []
def __del__(self, ):
''' on deletion, notify the observers '''
print "{0}.__del__".format(self)
# copy the observer list, then remove all references to the observers
# NEW - use weakrefs here, or they will not be properly deleted later
obs = [weakref.ref(o) for o in self.observers]
# clear all strong references to the observers
self.observers = []
# notify all observers that we were deleted
# ** only if they are not already deleted **
for o in obs:
if not o() == None:
o().deleting(self)
# NEW - second pass to allow the Manager to cleanup
# ** only if they are not already deleted **
for o in obs:
if not o() == None:
o().deleted(self)
def registerObserver(self, observer):
if observer not in self.observers:
self.observers.append(observer)
def removeObserver(self, observer):
self.observers.remove(observer)
def notifyObservers(self, message = None):
for observer in self.observers:
observer.update(self,message)
与以前相同,使用简化的字符串格式
class A(Subject):
''' A is just a subject '''
def __init__(self, name):
super(A,self).__init__()
self.name = name
def __str__(self):
return "A[ {0} ]".format(self.name)
B与以前相同
class B(Observer):
''' class B is an observer of A '''
def __init__(self, name, a):
self.name = name
# don't keep a reference to 'a', or 'a' will not be deleted!
a.registerObserver(self)
def __str__(self):
return "B[ {0} ]".format(self.name)
def __del__(self):
print("{0}.__del__".format(self))
def deleting(self, subject):
''' notification from the subject (A) that it is being deleted. I'm
assuming here that the subject is actually the one we registered in
__init__, since I couldn't store a reference or else __del__ would
not have been called! '''
print "B[{0}].deleting, subject={1}".format(self.name, subject)
del self
一些代码来执行文件:
if __name__ == '__main__':
mgr = Manager()
# keep strong references to the subjects, because
# we will delete them explicitly
a1 = A('a1')
a2 = A('a2')
mgr.monitorSubject(a1)
mgr.monitorSubject(a2)
# monitor observers directly, and do NOT keep
# strong references or they will not be deleted
mgr.monitorObserver( B('b1', a1) )
mgr.monitorObserver( B('b2', a1) )
mgr.monitorObserver( B('b3', a2) )
mgr.monitorObserver( B('b4', a2) )
# show the starting state
print mgr
print "Deleting a1..."
del a1
print mgr
print "Deleting a2..."
del a2
print mgr
并输出:
# OUTPUT (some newlines added)
#
# [Manager] Subjects:A[ a1 ],A[ a2 ], Observers:B[ b1 ],B[ b2 ],B[ b3 ],B[ b4 ]
#
# Deleting a1...
# A[ a1 ].__del__
# B[ b1 ].__del__
# B[ b2 ].__del__
# Removing dead subjects...
# Removing dead observers...
# [Manager] Subjects:A[ a2 ], Observers:B[ b3 ],B[ b4 ]
#
# Deleting a2...
# A[ a2 ].__del__
# B[ b3 ].__del__
# B[ b4 ].__del__
# Removing dead subjects...
# Removing dead observers...
#
# [Manager] Subjects:, Observers: