K
Kevin Little
#!/usr/bin/env python
'''
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 )
'''
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 )