开发者

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__)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜