Python:绑定一个未绑定的方法?

Posted

技术标签:

【中文标题】Python:绑定一个未绑定的方法?【英文标题】:Python: Bind an Unbound Method? 【发布时间】:2010-11-04 03:47:54 【问题描述】:

在 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 的方式将未绑定的方法绑定到实例并继续传递它而不调用它?

【问题讨论】:

@Christopher - 一种不受对象范围约束的方法,因此您必须明确地传递 self。 我特别喜欢“壮观的火焰,我哭了。” 【参考方案1】:

这会将self 绑定到handler

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

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

【讨论】:

是的,但这会调用方法。问题是我需要能够将绑定方法作为可调用对象传递。我有未绑定的方法和我希望绑定到的实例,但不知道如何在不立即调用它的情况下将它们放在一起 不,它不会,它只会在你执行 bound_handler() 时调用该方法。定义 lambda 不会调用 lambda。 您实际上可以使用 functools.partial 而不是定义 lambda。但是,它并不能解决确切的问题。您仍在处理function 而不是instancemethod @Alan: function 的第一个参数被你部分编辑了和instancemethod 有什么区别;鸭子打字看不出区别。 @LieRyan 不同之处在于您仍然没有处理基本类型。 functools.partial 删除一些元数据,例如__module__。 (另外我想声明一下,当我看到我对这个答案的第一条评论时,我真的很难受。)事实上,在我的问题中,我提到我已经在使用functools.partial,但我觉得必须有一个“更纯粹的” " 方式,因为很容易同时获得未绑定和绑定的方法。【参考方案2】:

这可以用types.MethodType 干净地完成。示例:

import types

def f(self): 
    print(self)

class C: 
    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>>

【讨论】:

+1 这很棒,但在您提供的 URL 的 python 文档中没有提到它。 +1,我不想在我的代码中调用魔术函数(即__get__)。我不知道你在哪个版本的 python 上测试过这个,但是在 python 3.4 上,MethodType 函数有两个参数。函数和实例。所以这个应该改成types.MethodType(f, C()) 来了!这是修补实例方法的好方法:wgt.flush = types.MethodType(lambda self: None, wgt) 它实际上在文档中提到,但在另一个答案的描述符页面中:docs.python.org/3/howto/descriptor.html#functions-and-methods【参考方案3】:

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

bound_handler = handler.__get__(self, MyWidget)

这里是 R. Hettinger 出色的 guide 描述符。


作为一个从Keith'scomment提取的独立示例:

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

【讨论】:

这很酷。我喜欢你可以省略类型并取回“绑定方法?.f”。 我喜欢这个解决方案胜过MethodType 一个,因为它在py3k 中的工作原理相同,而MethodType 的参数已经改变了一点。 因此,将函数绑定到类实例的函数:bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__)) 示例:class A: pass; a = A(); bind(a, bind, 'bind') 呵呵,你每天都能学到新东西。 @Kazark 至少在 Python 3 中,您也可以跳过提供类型,因为 __get__ 将从 object 参数中隐式获取该类型。我什至不确定提供它是否有任何作用,因为无论第一个参数是什么实例,我提供什么类型作为第二个参数都没有区别。所以bind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance)) 也应该做到这一点。 (虽然我个人更喜欢 bind 可用作装饰器,但那是另一回事。) 哇,从来不知道函数是描述符。这是一个非常优雅的设计,方法只是类'__dict__ 中的普通函数,属性访问通过普通描述符协议为您提供未绑定或绑定的方法。我一直认为这是type.__new__()期间发生的某种魔法【参考方案4】:

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

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

【讨论】:

是的,这与我原来的修复方法大致相同,即使用functools.partial(handler, self)【参考方案5】:

聚会迟到了,但我带着类似的问题来到这里:我有一个类方法和一个实例,并且想将实例应用于方法。

冒着过度简化 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 中的工作方式相同。 很抱歉没有更仔细地阅读您的原始要求。我偏爱(双关语)@brian-brazil 在***.com/a/1015355/558639 中给出的基于 lambda 的方法——它几乎是你能得到的最纯粹的。

以上是关于Python:绑定一个未绑定的方法?的主要内容,如果未能解决你的问题,请参考以下文章

python中怎样判断一个文件的类型

python TypeError:必须使用zoneAl实例作为第一个参数调用未绑定的方法zonecek()(啥都没有)

在 Python 3 中获取未绑定方法对象的定义类

带有 Django 1.7 迁移的 Python 2.7 未绑定方法

必须使用 fibo_ 实例作为第一个参数调用未绑定的方法 f()(改为获取 classobj 实例)

Python方法绑定——Unbound/Bound method object的一些梳理