471,313 Members | 1,938 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,313 software developers and data experts.

adding class functionality, nested scoping

Hi,

regarding automatically adding functionality to a class (basically taken
from the cookbook recipee) and Python's lexical nested scoping I have a question wrt this code:

#-----------------
import types

# minor variation on cookbook recipee
def enhance_method(cls, methodname, replacement):
'replace a method with an enhancement'
method = getattr(cls, methodname)
def _f(*args, **kwargs):
return replacement(method, *args, **kwargs)
_f.__name__ = methodname
setattr(cls, methodname, types.MethodType(_f, None, cls))

# loop over class dict and call enhance_method() function
# for all methods to modify
def enhance_all_methods(cls, replacement):
for methodname in cls.__dict__:
if not methodname.startswith("__"):
method = getattr(cls, methodname)
def _f(*args, **kwargs):
return replacement(method, *args, **kwargs)
_f.__name__ = methodname
enhance_method(cls, methodname, replacement)
# Does not work: all enhanced methods only call the last wrapped originial
# method. It seems the name 'method' in the surrounding scope of the
# def _(...) function definition only refers to the last loop value(?)
def ERRONEOUS_enhance_all_methods(cls, replacement):
for methodname in cls.__dict__:
if not methodname.startswith("__"):
method = getattr(cls, methodname)
def _f(*args, **kwargs):
return replacement(method, *args, **kwargs)
_f.__name__ = methodname
setattr(cls, methodname, types.MethodType(_f, None, cls))
class Foo(object):
def foo(self, x):
print "foo", x

def bar(self, x):
print "bar", x
def logme(method, *args, **kwargs):
print "-->", method.__name__, args, kwargs
try:
return method(*args, **kwargs)
finally:
print "<--"
#enhance_all_methods(Foo, logme)
ERRONEOUS_enhance_all_methods(Foo, logme)

foo = Foo()
foo.foo(2)
foo.bar(2)
#-----------------

....give this output:
>>foo = Foo()
foo.foo(2)
--foo (<__main__.Foo object at 0x1b08f0>, 2) {}
foo 2
<--
>>foo.bar(2)
--foo (<__main__.Foo object at 0x1b08f0>, 2) {}
foo 2
<--
>>>
So, while using enhance_all_methods() to add functionality does work, ERRONEOUS_enhance_all_methods() does not. Why is this? Is the explanation I tried to give in the code comment on the right track:

# Does not work: all enhanced methods only call the last wrapped originial
# method. It seems the name 'method' in the surrounding scope of the
# def _(...) function definition only refers to the last loop value(?)

Thanks for any hint,
Holger
--
Psssst! Schon vom neuen GMX MultiMessenger gehört?
Der kann`s mit allen: http://www.gmx.net/de/go/multimessenger?did=10
Jan 4 '08 #1
1 1464
On Jan 4, 10:43*am, jh...@gmx.de wrote:
Hi,
Hi

[...]
# Does not work: all enhanced methods only call the last wrapped originial
# method. It seems the name 'method' in the surrounding scope of the
# def _(...) function definition only refers to the last loop value(?)
def ERRONEOUS_enhance_all_methods(cls, replacement):
* * for methodname in cls.__dict__:
* * * * if not methodname.startswith("__"):
* * * * * * method = getattr(cls, methodname)
* * * * * * def _f(*args, **kwargs):
* * * * * * * * return replacement(method, *args, **kwargs)
* * * * * * _f.__name__ = methodname
* * * * * * setattr(cls, methodname, types.MethodType(_f, None, cls))
This is normal: After ERRONEOUS_enhance_all_methods is called, the
value method is the one from the last iteration of the loop. All
subsequent references to 'method' (in function _f) will return that
last value. To solve this problem you need to fix the object 'method'
is bound to in function _f:

def enhance_all_methods(cls, replacement):
# The following binds 'method' to its current value in _f
def replace(method):
def _f(*args, **kwargs):
return replacement(method, *args, **kwargs)
return _f
for methodname in cls.__dict__:
if not methodname.startswith("__"):
_f = replace(getattr(cls, methodname))
_f.__name__ = methodname
setattr(cls, methodname, types.MethodType(_f, None, cls))

Of course this looks more like your first version, which trims down to
the following and is probably a better option:

def enhance_all_methods(cls, replacement):
for methodname in cls.__dict__:
if not methodname.startswith("__"):
enhance_method(cls, methodname, replacement)

HTH

--
Arnaud

Jan 4 '08 #2

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

6 posts views Thread by Andy Baker | last post: by
9 posts views Thread by OKB (not okblacke) | last post: by
2 posts views Thread by Victor Liu | last post: by
7 posts views Thread by newbiecpp | last post: by
3 posts views Thread by Carmella | last post: by
19 posts views Thread by Jamey Shuemaker | last post: by
17 posts views Thread by Jef Driesen | last post: by
reply views Thread by rosydwin | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.