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 TypeError:必须使用zoneAl实例作为第一个参数调用未绑定的方法zonecek()(啥都没有)
带有 Django 1.7 迁移的 Python 2.7 未绑定方法