Adding bound methods dynamically... CORRECTED

Kevin Little kevin_little at comcast_net.com
Wed Aug 31 04:39:50 CEST 2005


#!/usr/bin/env python

# Sorry... :} cut/paste error fixed...

'''
I want to dynamically add or replace bound methods in a class.  I want
the modifications to be immediately effective across all instances,
whether created before or after the class was modified.  I need this
to work for both old ('classic') and new style classes, at both 2.3
and 2.4.  I of course want to avoid side effects, and to make the
solution as light-weight as possible.

Question for the experts: Is the solution coded in AddBoundMethod()
acceptable to the Pythonian Gods? :) It does seem to work -- tested
at 2.3.5 (RH Linux) and 2.4.1 (WinXP)

Is there a more pythonic way that's as straight forward?
'''

def AddBoundMethod( cls, name, method ):
   '''
   Dynamically add to the class 'cls' a bound method.

   Invoking this method instantly adds (or overwrites) the
   bound method identified by 'name' with the code contained in
   'method', EVEN FOR PRE-EXISTING INSTANCES OF THE CLASS. The
   'method' parameter should be a non-class function that has 'self'
   as its first parameter.
   '''

   try: types
   except NameError: import types

   #
   # this is the crux of this example, short and sweet...
   #
   exec "%s.%s = types.MethodType( method, None, %s )" \
          % ( cls.__name__, name, cls.__name__ )

#
# The remainder (50x longer than the solution!) is test code...
#

# one new-style class...
class NewStyleClass( object ):

   def __init__ ( self, objname ):
      print "Created a NewStyleClass, id %d, %s" % \
            ( id( self ), objname )
      self.objname = objname

   def ExistingMethod( self, msg ):
      print "Original ExistingMethod, id %d, %s: '%s'" % \
            ( id( self ), self.objname, msg )

# one 'classic' style class...
class OldStyleClass:

   def __init__ ( self, objname ):
      print "Created a OldStyleClass, id %d, %s" % \
            ( id( self ),  objname )
      self.objname = objname

   def ExistingMethod( self, msg ):
      print "Original ExistingMethod, id %d, %s: '%s'" % \
            ( id( self ), self.objname, msg )

# two non-class functions that *look* like bound methods in a class;
# one returns a value, the other just outputs a string...
def NeverInOriginalClass( self, msg ):
   return "Never in original class, id %d, %s: '%s'" % \
          ( id( self ), self.objname, msg )

def NewExistingMethod( self, msg ):
   print  "REPLACED ExistingMethod, id %d, %s: '%s'" % \
          ( id( self ), self.objname, msg )

# a test routine...
def Test( cls ):

   print "--- %s ----------------------------------------------" % \
         cls.__name__

   print "type of class %s is '%s'" % ( cls.__name__, type( cls ) )

   before_change = cls('instance created before change')

   print "type of object before_change is '%s'" % type(before_change)

   # 'A' shows that we start with an existing method....
   before_change.ExistingMethod( 'A' )

   print "*** Replacing bound method 'ExistingMethod'..."

   AddBoundMethod( cls, "ExistingMethod", NewExistingMethod )

   after_change = cls( 'instance created AFTER  change' )

   print "type of after_change is '%s'" % type( after_change )

   # 'B' and 'C' show we've replaced an existing method, both on
   # pre-existing instances and instances created after using
   # AddBoundMethod()
   before_change.ExistingMethod( 'B' )
   after_change.ExistingMethod( 'C' )

   print "*** Adding new bound method 'AddedMethod'..."

   AddBoundMethod( after_change.__class__, "AddedMethod",
                   NeverInOriginalClass )

   # 'D' and 'E' show we've added a brand new method, both on
   # pre-existing instances and instances created after using
   # AddBoundMethod()
   print "%s" % before_change.AddedMethod( 'D' )
   print "%s" % after_change.AddedMethod( 'E' )


if __name__ == '__main__':

   Test( OldStyleClass )
   Test( NewStyleClass )




More information about the Python-list mailing list