I am attempting to implement a very simple observer pattern in python.
Here is my Observer
class (it's really just an interface and I guess I don't actually need it):
class Observer():
def update(self,subject,message): pass
And my Subject
class (aka Observable
, but I prefer 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)
Class A
contains a nested DelNotifier
class, which is a subclass of Subject
. The idea that when a class A
object is deleted (actually garbage collected, since it is in the __del__
method), A.DelNotifier
will notify all of its observers of the deletion.
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.")
Class B
contains a nested AObserver
class, which is a subclass of Observer
and will receive the message from the class A.DelNotifier
subject when an A
has been deleted (again, in actuality this happens when the A
object has been garbage collected):
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!")
This design works when I call __del__()
directly, however, some of the objects seem to be gc'd a second time when the session exits:
>>> 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!
Another problem, and I think this is more important, is that when I del
a class A
item from a list, the item is not immediately garbage collected and I cannot be sure that any registered B
items have been deleted:
>>> 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!
In addition to these problems, I am aware of the many many problems inherent in relying on the __del__
method to do anything other than cleaning up an object after it has been gc'd, and that it should probably be avoided for the purposes I am trying to employ. But I don't know of another way.
What would be a better way to do this? I have thought about trying to use a context manager (with
) to delete things after I'm done using them, but I have no experience doing that. If that would be a good option, how would I go about doing that? What would it look like?
EDIT: Clarification of desired behavior
I'll attempt to clear up some of the (understandable) confusion.
I've simplified the code quite a bit above, but B
is an object that depends on an object A
. If a B
's A
goes away, that B
should go away. I'll have some container (using a list
here) of As and of 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!
Also see my previous question from the other day which helped lead me to this whole idea of using the observer pattern in the first place.