Adding bound methods dynamically...

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 )
 
K

Kevin Little

#!/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 )
 
D

Devan L

Kevin said:
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)

Code:
[/QUOTE]

I'm not an expert, but why do you need to dynamically add or replace
bound methods?
 
G

Gregory Bond

Kevin said:
I want to dynamically add or replace bound methods in a class. I want


I asked a seemingly-unrelated question a week or so ago, and learned
something interesting:

Python 2.3.4 (#2, Jul 12 2004, 12:46:36)
[GCC 3.3] on sunos5
Type "help", "copyright", "credits" or "license" for more information.... print "foo called"
...... pass
...
I.e. assigning a normal function object to a class object turns it into
a member function!

You can read more in the thread with the subject 'keeping a ref to a
non-member function in a class'.
 
B

bruno modulix

Devan said:
(snip)

I'm not an expert, but why do you need to dynamically add or replace
bound methods?

To modify the behaviour at runtime ?-)

There are a lot of idioms/patterns in dynamic languages that seems
somewhat alien at first, then become an obvious solution. When I
discovered first-class functions and anonymous functions with Python
some years ago, I wondered what could be the use of such things. Now I
couldn't live without...
 
M

Mike Meyer

bruno modulix said:
To modify the behaviour at runtime ?-)

There are a lot of idioms/patterns in dynamic languages that seems
somewhat alien at first, then become an obvious solution. When I
discovered first-class functions and anonymous functions with Python
some years ago, I wondered what could be the use of such things. Now I
couldn't live without...

Yes, but rather than going through the contortions you do to bind a
new method into place, why not make the method in question act as a
proxy for the real method? After all, with first-class functions,
that's easy.

<mike
 
D

Diez B. Roggisch

Yes, but rather than going through the contortions you do to bind a
new method into place, why not make the method in question act as a
proxy for the real method? After all, with first-class functions,
that's easy.

Because you don't have to write that proxy. Pure lazyness :)


Diez
 
B

bruno modulix

Mike said:
(snip)

Yes, but rather than going through the contortions you do to bind a
new method into place,

Contortion ? Which contortion ?

class Foo(object):
def __init__(self, name):
self.name = name

def greet1(self, who):
print "hello %s, this is %s" % (who, self.name)

def greet2(self, who):
print "Yo %s, have a drink with Daddy %s" % (who, self.name)

f = Foo('Python')

Foo.greet = greet1
f.greet('Monty')
Foo.greet = greet2
f.greet('Monty')
why not make the method in question act as a
proxy for the real method?

Please show us an implementation.
 
B

Bruno Desthuilliers

Kevin Little a écrit :

Oops, sorry, forgot to answer
'''
I want to dynamically add or replace bound methods in a class. (snip)
Is there a more pythonic way that's as straight forward?

What's wrong with:

class Foo:
pass

def method(self):
print "%s" % self

f = Foo()
Foo.method = method
f.method()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top