Demonstration of Weak Reference Extension Module

The following listing demonstrates a simple use of weakref module:

Python 2.0b2 (#6, Sep 26 2000, 14:59:21) [MSC 32 bit (Intel)] on win32
Type "copyright", "credits" or "license" for more information.
>>> import weakref
>>> class Foo: pass
...
>>> f = Foo ()
>>> wr = weakref.new (f)
>>> wr
A weak reference to an object of type 'instance'.
>>> f1 = wr.newref ()
>>> f1
<__main__.Foo instance at 0081B63C>
>>> f1 == f
1
>>> del f1
>>> del f
>>> wr
A stale weak reference.
>>> type(wr.newref ())
(type, 'None')
The following listing demonstrates the circular reference problem:
       1    # !/usr/local/bin/python
       2    # 
       3    # Filename: circref_demo.py
       4    # Copyright (c) Alex Shindich, 2000
       5    # Author: Alex Shindich
       6    # License: The author is making this software available on an "AS IS" basis.
       7    #   There are no warranties regarding this software. Use it at your own risk!
       8    #   Permission to use, copy, modify, and distribute this software and its
       9    #   documentation for any purpose and without fee is hereby granted without
      10    #   any restrictions.
      11    #
      12    # Purpose: This program demonstrates the circular reference problem.
      13    
      14    class Subject:
      15        """This class implements a very simple subject for observer pattern."""
      16    
      17        def __init__ (self):
      18            self.__observers = []
      19    
      20        def addObserver (self, observer):
      21            if observer.notify not in self.__observers:
      22                self.__observers.append (observer.notify)
      23                print "Added observer: %s" % str(observer)
      24    
      25        def removeObserver (self, observer):
      26            if observer.notify in self.__observers:
      27                print "Removed observer: %s" % str(observer)
      28                self.__observers.remove (observer.notify)
      29            else:
      30                print "Cannot remove an unknown observer: %s" % str(observer)
      31    
      32        def notifyAll (self, msg):
      33            if self.__observers:
      34                for notify in self.__observers:
      35                    notify (msg)
      36            else:
      37                print "No observers to notify."
      38    
      39    class Observer:
      40        """This class implements a very simple observer for observer pattern."""
      41    
      42        def __init__(self, subject):
      43            self.__subject = subject
      44            self.__subject.addObserver (self)
      45    
      46        def __del__ (self):
      47            self.__subject.removeObserver (self)
      48            print "Observer destroyed: %s" % str(self)
      49    
      50        def notify (self, msg):
      51            print "Notification received from the subject:\n\t'%s'" % msg
      52    
      53    def main ():
      54        # Create the subject
      55        sub = Subject ()
      56        # Create an observer
      57        obs = Observer (sub)
      58        # Notify all the observers
      59        sub.notifyAll ("Python rules!")
      60        # Delete an observer
      61        del obs
      62        # Notify all the observers
      63        sub.notifyAll ("There shouldn't be any observers at this point.")
      64    
      65    
      66    if __name__ == '__main__':
      67        main ()
The Following is the output of this program:

Added observer: <__main__.Observer instance at 007F886C>
Notification received from the subject:
	'Python rules!'
Notification received from the subject:
	'There shouldn't be any observers at this point.'


The following listing demonstrates how this problem can be solved using weak references:

       1    # !/usr/local/bin/python
       2    # 
       3    # Filename: circref_demo.py
       4    # Copyright (c) Alex Shindich, 2000
       5    # Author: Alex Shindich
       6    # License: The author is making this software available on an "AS IS" basis.
       7    #   There are no warranties regarding this software. Use it at your own risk!
       8    #   Permission to use, copy, modify, and distribute this software and its
       9    #   documentation for any purpose and without fee is hereby granted without
      10    #   any restrictions.
      11    #
      12    # Purpose: This program demonstrates the use of weakref module.
      13    
      14    try:
      15        import weakref
      16    except ImportError, ex:
      17        print ex
      18        print "Please make sure that weakref module is in the Python library path."
      19        import sys
      20        sys.exit (0)
      21       
      22    
      23    
      24    class Subject:
      25        """This class implements a very simple subject for observer pattern."""
      26    
      27        class Callback:
      28            """This class represents an observer  callback reference."""
      29    
      30            def __init__ (self, instanceMethod):
      31                self.__func  = instanceMethod.im_func
      32                self.__ob_self = weakref.new (instanceMethod.im_self)
      33    
      34            def __call__ (self, *args):
      35                ob_self = self.__ob_self.newref ()
      36                func = self.__func
      37                if ob_self and func:
      38                    apply (func, (ob_self,) + args)
      39                
      40        def __init__ (self):
      41            self.__observers = {}
      42    
      43        def addObserver (self, observer):
      44            # Create a weak reference that we will use for dictionary
      45            wr_observer = weakref.new (observer)
      46            # Check to see if the weak reference is in the dictionary already
      47            if not self.__observers.has_key (wr_observer):
      48                # Create a callback instance and add it to the dictionary
      49                cb = Subject.Callback (observer.notify)
      50                self.__observers[wr_observer]= cb
      51                print "Added observer: %s" % str(observer)
      52    
      53        def removeObserver (self, observer):
      54            # Create a weak reference that we will use for dictionary
      55            wr_observer = weakref.new (observer)
      56            # Check to see if the weak reference is in the dictionary
      57            if self.__observers.has_key (wr_observer):
      58                print "Removed observer: %s" % str(observer)
      59                # Delete the weak reference from the map
      60                del self.__observers[wr_observer]
      61            else:
      62                print "Cannot remove an unknown observer: %s" % str(observer)
      63    
      64        def notifyAll (self, msg):
      65            if self.__observers:
      66                for notify in self.__observers.values ():
      67                    notify (msg)
      68            else:
      69                print "No observers to notify."
      70    
      71    class Observer:
      72        """This class implements a very simple observer for observer pattern."""
      73    
      74        def __init__(self, subject):
      75            self.__subject = subject
      76            self.__subject.addObserver (self)
      77    
      78        def __del__ (self):
      79            self.__subject.removeObserver (self)
      80            print "Observer destroyed: %s" % str(self)
      81    
      82        def notify (self, msg):
      83            print "Notification received from the subject:\n\t'%s'" % msg
      84    
      85    def main ():
      86        # Create the subject
      87        sub = Subject ()
      88        # Create an observer
      89        obs = Observer (sub)
      90        # Notify all the observers
      91        sub.notifyAll ("Python rules!")
      92        # Delete an observer
      93        del obs
      94        # Notify all the observers
      95        sub.notifyAll ("There shouldn't be any observers at this point.")
      96    
      97    if __name__ == '__main__':
      98        main ()

The following is the output of this program:


Added observer: <__main__.Observer instance at 008254EC>
Notification received from the subject:
	'Python rules!'
Removed observer: <__main__.Observer instance at 008254EC>
Observer destroyed: <__main__.Observer instance at 008254EC>
No observers to notify.

Note that the problem is really solved through the use of Callback class.