Python:绑定未绑定方法?


117

在Python中,有没有办法绑定未绑定的方法而不调用它?

我正在编写一个wxPython程序,对于某个类,我决定将所有按钮的数据分组为一个类级别的元组列表是很不错的,如下所示:

class MyWidget(wx.Window):
    buttons = [("OK", OnOK),
               ("Cancel", OnCancel)]

    # ...

    def Setup(self):
        for text, handler in MyWidget.buttons:

            # This following line is the problem line.
            b = wx.Button(parent, label=text).Bind(wx.EVT_BUTTON, handler)

问题是,因为所有的值handler都是未绑定方法,所以我的程序爆炸得很厉害,我哭了。

我正在网上寻找解决方案,该方案应该是一个相对简单,可解决的问题。不幸的是我找不到任何东西。现在,我正在functools.partial解决此问题,但是没有人知道是否存在一种干净,健康,Pythonic的方式将未绑定的方法绑定到实例并继续传递它而不调用它吗?


6
@Christopher-一种方法,该方法未绑定到从中吸取对象的范围,因此您必须显式传递self。
艾登·贝尔

2
我特别喜欢“壮观的火焰,我哭泣”。
jspencer

Answers:


174

所有函数也是描述符,因此您可以通过调用它们的__get__方法来绑定它们:

bound_handler = handler.__get__(self, MyWidget)

这是R. Hettinger 关于描述符的出色指南


作为一个独立的例子,请参考Keith的 评论

def bind(instance, func, as_name=None):
    """
    Bind the function *func* to *instance*, with either provided name *as_name*
    or the existing name of *func*. The provided *func* should accept the 
    instance as the first argument, i.e. "self".
    """
    if as_name is None:
        as_name = func.__name__
    bound_method = func.__get__(instance, instance.__class__)
    setattr(instance, as_name, bound_method)
    return bound_method

class Thing:
    def __init__(self, val):
        self.val = val

something = Thing(21)

def double(self):
    return 2 * self.val

bind(something, double)
something.double()  # returns 42

3
太酷了。我喜欢您可以省略类型并取回“绑定方法?.f”的方法。
科伊夫

我喜欢这种解决方案MethodType,因为它在py3k中的工作原理相同,而MethodType的参数已经改变了一点。
bgw

10
因此,将函数绑定到类实例的函数:bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__))示例:class A: pass; a = A(); bind(a, bind, 'bind')
Keith Pinson

2
you,您每天都学到新东西。@Kazark至少,在Python 3中,您也可以跳过提供类型的操作,因为__get__将从对象参数中隐式地获取该类型。我什至不确定提供它是否可以做任何事情,因为无论第一个参数是什么实例,我提供的第二个参数类型都没有区别。因此也bind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance))应该做到这一点。(尽管我bind个人更希望可以用作装饰器,但这是另一回事。)
JAB 2014年

1
哇,从来都不知道函数是描述符。这是一个非常优雅的设计,方法只是类中的普通函数,__dict__而属性访问可通过常规描述符协议为您提供未绑定或绑定的方法。我一直以为这是发生在某种魔术上的type.__new__()
JaredL

81

可以使用types.MethodType干净地完成此操作。例:

import types

def f(self): print self

class C(object): pass

meth = types.MethodType(f, C(), C) # Bind f to an instance of C
print meth # prints <bound method C.f of <__main__.C object at 0x01255E90>>

10
+1很棒,但是您提供的URL的python文档中没有对它的引用。
凯尔·维尔德

6
+1,我希望代码中没有调用魔术函数(即__get__)。我不知道您在哪个版本的python上进行过测试,但是在python 3.4上,该MethodType函数带有两个参数。函数和实例。因此,应将其更改为types.MethodType(f, C())
丹·米隆

这里是!这是修补实例方法的好方法:wgt.flush = types.MethodType(lambda self: None, wgt)
Winand 2015年

2
它实际上是在文档中提到的,但在其他答案的描述符页面中:docs.python.org/3/howto/descriptor.html#functions-and-methods
kai

9

创建一个带有self的闭包在技术上不会绑定该函数,但这是解决相同(或非常相似)潜在问题的另一种方法。这是一个简单的例子:

self.method = (lambda self: lambda args: self.do(args))(self)

1
是的,这与我使用的原始修复程序大致相同functools.partial(handler, self)
Dan Passaro

7

这将绑定selfhandler

bound_handler = lambda *args, **kwargs: handler(self, *args, **kwargs)

这是通过将self第一个参数传递给函数来实现的。object.function()只是语法糖function(object)


1
是的,但这会调用该方法。问题是我需要能够将绑定方法作为可调用对象传递。我有未绑定的方法和想要绑定到的实例,但无法弄清楚如何在不立即调用的情况下将它们放在一起
Dan Passaro

9
不,不是这样,它只有在执行bound_handler()时才会调用该方法。定义lambda不会称为lambda。
brian-brazil

1
您实际上可以使用functools.partial而不是定义lambda。但是,它不能解决确切的问题。您仍在处理function而不是instancemethod
艾伦·梅尔

5
@Alan:function您偏爱其第一个参数的和之间有什么区别instancemethod?鸭子打字看不出区别。
Lie Ryan

2
@LieRyan的不同之处在于您仍然没有处理基本类型。functools.partial删除一些元数据,例如__module__。(也想记录一下,当我对这个答案的第一个评论时,我非常畏缩。)实际上,在我的问题中,我提到我已经在使用,functools.partial但是我觉得必须采用一种“更纯粹”的方式,因为很容易获得未绑定和绑定的方法。
丹·帕萨罗

1

晚了,但是我来这里有一个类似的问题:我有一个类方法和一个实例,并且想要将该实例应用于该方法。

冒着过于简化OP问题的风险,我最终做了一些不太神秘的事情,这可能会对到这里来的其他人有用(注意:我正在使用Python 3 – YMMV工作)。

考虑这个简单的类:

class Foo(object):

    def __init__(self, value):
        self._value = value

    def value(self):
        return self._value

    def set_value(self, value):
        self._value = value

这是您可以使用的方法:

>>> meth = Foo.set_value   # the method
>>> a = Foo(12)            # a is an instance with value 12
>>> meth(a, 33)            # apply instance and method
>>> a.value()              # voila - the method was called
33

这并不能解决我的问题-这是我希望meth可以调用而不需要将a参数发送给它(这就是我最初使用的原因functools.partial)-但这是可取的,如果您不需要传递方法并且可以当场调用它。另外这款作品以同样的方式在Python 2因为它在Python 3
丹Passaro

很抱歉没有更仔细地阅读您的原始要求。我只是偏爱(双关语意)stackoverflow.com/a/1015355/558639中 @ brian-brazil给出的基于lambda的方法-尽可能地纯正。
fearless_fool
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.