Creating a function from a member of an instance for another instance in python
Imagine that i have f
which is a function of a member of a class instance:
开发者_JS百科class A:
def b(self):
print 'hey'
a = A()
f = a.b
If I have another instance of the same class, let's say c = A()
how can I reconstruct a new ff
only using f
and c
, so calling ff()
would result in c.b()
instead of a.b()
?
c = A()
ff = some_python_kungfu(f,c)
ff() #it is calling c.b()
Can you use a method reference for the class instead of the instance reference?
class A:
def whoami(self):
print 'I am %s' % id(self)
a = A()
c = A()
func = A.whoami
func(a)
func(c)
So you want to know how to rebind an already bound method to another instance, using only the bound method and the other instance. It can be done like this:
def some_python_kungfu(meth, obj):
return type(meth)(meth.__func__, obj, obj.__class__)
The __func__
attribute is really the same as Ned Batchelders im_func
, but __func__
is forward-compatible with python 3.
There is one case where this will not work: methods of built-in classes. The __func__
and im_func
attributes are only available on user-defined classes. Therefore, this will fail:
a = "that's no ordinary rabbit"
b = "consult the book of armaments"
b_split = some_python_kungfu(a.split, b)
A slight modification of Ned's solution will work on both built-in and user-defined classes:
def some_python_kungfu(meth, obj):
return getattr(obj, meth.__name__)
So will this always work then? Well... no, but the stumbling block a rather obscure and (I guess) seldom occuring problem: if the name of the method (meth.__name__
) is not the same as the name it has in the class dictionary ('b'
), then getattr
will either return the wrong attribute or raise an AttributeError
. For example:
def external(self):
pass
class A(object):
b = external
Here A.b.__name__ == 'external'
instead of 'b'
, so getattr(obj, 'external')
will be called instead of getattr(obj, 'b')
.
While both previous approaches have problems, one with built-in classes and one with patched-together classes, both problems do not occur simultaneously in any circumstance. Therefore, a combination will work in all cases:
def some_python_kungfu(meth, obj):
try:
return type(meth)(meth.__func__, obj, obj.__class__)
except AttributeError:
# meth is a built-in method, so meth.__name__ is always correct
return getattr(obj, meth.__name__)
As explained elsewhere on this page, your best bet would probably be to ignore this whole mess and do it some cleaner way, like for instance using the unbound methods and passing in the first argument (self
) manually, as in Cixates answer. But who knows, this may prove useful to some of you some day perhaps, in a somewhat bizarre set of circumstances. ;)
I'm not sure this would work in all cases, but:
def some_python_kungfu(meth, obj):
"""Get a bound method on `obj` corresponding to the method `meth`."""
return getattr(obj, meth.im_func.__name__)
精彩评论