当主题被删除时,使用观察者模式删除观察者"在Python中

时间:2014-11-25 22:35:31

标签: python python-3.x

我试图在python中实现一个非常简单的observer pattern

这是我的Observer课程(它实际上只是一个界面,我想我实际上并不需要它):

class Observer():
    def update(self,subject,message): pass

我的Subject课程(又名Observable,但我更喜欢Subject):

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.DelNotifier A时会收到来自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__()

当我直接调用>>> 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! 时,此设计有效,但是,当会话退出时,某些对象似乎是第二次gc'd:

del

另一个问题,我认为这更重要的是,当我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__

除了这些问题之外,我知道依赖with方法除了在gc'd之后清理对象以外的其他任何事情所固有的许多问题,并且它应该可能为了我想要的目的而避免使用。但我不知道另一种方式。

最好的办法是什么?我已经考虑过尝试使用上下文管理器(B)在我使用它们之后删除它们,但我没有这方面的经验。如果这是一个不错的选择,我将如何做到这一点?它会是什么样子?

编辑:澄清期望的行为

我会尝试澄清一些(可理解的)混淆。

我已经将代码简化了一些,但是A是一个依赖于对象B的对象。如果A的{​​{1}}消失,则B应该消失。我将有一些容器(在这里使用list)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帮助我了解了首先使用观察者模式的整个想法。

2 个答案:

答案 0 :(得分:1)

警告:我在2.7.3工作,所以其中一些可能会有所不同。

我有一些代码似乎比你拥有的更好一些。希望它能提出一些相关的观点。

首先,观察者界面显示一个deleting事件,当主体离开时将调用该事件。正如您所指出的,这不是必需的,但我想明确声明我的接口。

class Observer(object):
    def update(self,subject,message): pass

    def deleting(self,subject):
        ''' the subject is being deleted '''
        pass

接下来,Subject类将负责处理__del__并通知所有观察者。这有点简化,并且使代码远离A,并从您的初始示例中删除所有内部类。在Subject的{​​{1}}方法中,我复制观察者,清除它们(删除所有引用),然后调用__del__方法以允许观察者自行删除。

deleting

我遗漏了class Subject(object): def __init__(self): self.observers = [] def __del__(self, ): ''' on deletion, notify the observers ''' print "Subject.__del__".format(self) # copy the observer list, then remove all references to the observers obs = self.observers[:] self.observers = [] # notify all observers that we were deleted for o in obs: o.deleting(self) def registerObserver(self, observer): if observer not in self.observers: self.observers.append(observer) removeObserver因为我没有在这个解决方案中使用它们(显然它们非常有用)。

notifyObservers只是一个简单的A,除此之外不提供任何命名功能。为了简单起见,我删除了只有Subject才能注册到B的限制,我认为没有必要解决这个问题。

A

然后class A(Subject): ''' A is just a subject ''' def __init__(self, name): super(A,self).__init__() self.name = name def __str__(self): return "A[name={0}]".format(self.name) 作为观察者在创作时自动将自己添加为A的观察者。当删除观察到的主题时,通过B方法收到通知。由于只有一个被观察的主题,我认为它是deleting传递给A并删除__init__。重要的是(至少在2.7中)不在self内存储a的引用,或者B可能永远不会被gc调用(这可能是我对{的误解{1}})。

__del__

使用此代码,我得到输出:

gc

看起来更像你想要的,除了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[name={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.deleting, subject={0}".format(subject) del self 不会立即被垃圾收集。

编辑:添加了基于进一步澄清行为的示例

>>> from main import A, B
>>> a = A('a')
>>> B('b1', a)
<main.B object at 0x00000000022938D0>
>>> B('b2', a)
<main.B object at 0x0000000002293B70>

>>> del a
Subject.__del__
B.deleting, subject=A[name=a]
B.deleting, subject=A[name=a]
B[name=b1].__del__

>>> import gc
>>> gc.collect()
B[name=b2].__del__
0

b2数组中删除项目会从列表中删除该项目,从而减少引用计数,并删除主题>>> from main import A, B >>> >>> As = [A('a'+str(i)) for i in range(10)] >>> Bs = [B('b'+str(i),As[i]) for i in range(10)] ,通知As主题已删除。 a1然后会自行调用b1,但由于b1数组中的引用而未被删除。

del

Bs数组中的引用设置为>>> del As[1] Subject.__del__ B.deleting, subject=A[name=a1] 会将引用次数减少为0,从而调用Bs

None

显示__del__数组需要手动清理

>>> Bs[1] = None
B[name=b1].__del__

并且Bs没有任何工作要做:

>>> len(As)
9
>>> len(Bs)
10

>>> del Bs[1]
>>> len(Bs)
9

其余内容在gc

上清理完毕

答案 1 :(得分:1)

这是一个很大的代码差异,可以说明您自动维护引用列表的要求,以及删除引用时的清理。我已经添加了一个Manager类来完成这个,以及第二个传递deleted()事件,它是管理器清理它正在维护的列表的一个钩子。我在这里重新发布了完整的修改后的代码,因为更新我以前的答案并非易事。

我相信这完全满足了你提出的问题。也许不是为什么你首先需要这个的原因,但我想你在另一个问题上问过这个。

我们需要弱引用来使这项工作:

import weakref

<{em> 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])
        )

评论中注明了主题的差异,但主要是第二次调用deleteddeleting的第一次通过通知观察者,而deleted的第二次通过通知经理。此外,__del__例程使用弱引用进行迭代,因为其中一些被删除。

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: