如何绑定未绑定的方法而不调用它?

发布于 2024-07-25 09:22:15 字数 653 浏览 7 评论 0原文

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

In Python, is there a way to bind an unbound method without calling it?

I am writing a wxPython program, and for a certain class I decided it would be nice to group the data of all of my buttons together as a class-level list of tuples, like so:

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)

The problem is, since all of the values of handler are unbound methods, my program explodes in a spectacular blaze and I weep.

I was looking around online for a solution to what seems like should be a relatively straightforward, solvable problem. Unfortunately I couldn’t find anything. Right now, I am using functools.partial to work around this, but does anyone know if there’s a clean-feeling, healthy, Pythonic way to bind an unbound method to an instance and continue passing it around without calling it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

秋日私语 2024-08-01 09:22:15

所有函数也是描述符,因此您可以通过调用它们的 __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

All functions are also descriptors, so you can bind them by calling their __get__ method:

bound_handler = handler.__get__(self, MyWidget)

Here's R. Hettinger's excellent guide to descriptors.


As a self-contained example pulled from Keith's comment:

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
丘比特射中我 2024-08-01 09:22:15

这可以通过 types.MethodType< 来完成/a>:

import types

bound_handler = types.MethodType(handler, self)

This can be done with types.MethodType:

import types

bound_handler = types.MethodType(handler, self)
久光 2024-08-01 09:22:15

使用闭包,也称为封闭表达式(与开放表达式相反),这是一个没有自由变量

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

这里handlerself是内部lambda表达式中的自由变量,外部 lambda 表达式中的绑定变量,而 argskwargs 是两个表达式中的绑定变量内部和外部 lambda 表达式,因此外部 lambda 表达式是一个闭包。

With a closure, also known as a closed expression (as opposed to an open expression), which is an expression without free variables:

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

Here handler and self are free variables in the inner lambda expression and bound variables in the outer lambda expression, and args and kwargs are bound variables in both the inner and outer lambda expressions, so the outer lambda expression is a closure.

夏末的微笑 2024-08-01 09:22:15

这会将 self 绑定到 handler

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

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

This will bind self to handler:

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

This works by passing self as the first argument to the function. obj.f() is just syntactic sugar for f(obj).

花开浅夏 2024-08-01 09:22:15

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

冒着过度简化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

Late to the party, but I came here with a similar question: I have a class method and an instance, and want to apply the instance to the method.

At the risk of oversimplifying the OP's question, I ended up doing something less mysterious that may be useful to others who arrive here (caveat: I'm working in Python 3 -- YMMV).

Consider this simple class:

class Foo(object):

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

    def value(self):
        return self._value

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

Here's what you can do with it:

>>> 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
め七分饶幸 2024-08-01 09:22:15

扩展@Keith Pinson的答案,它使用闭包和@brian-brazil 的答案 没有,这就是为什么前者是正确的。

带闭包的示例:

def handler(self, *args, **kwargs):
    return self


self = True
bound_handler = (lambda handler, self:
    lambda *args, **kwargs: handler(self, *args, **kwargs)
)(handler, self)
bound_handler()  # returns True
self = False
bound_handler()  # returns True
handler = print
bound_handler()  # returns True

外部 lambda 表达式已关闭,因此其绑定变量 handlerself 无法重新绑定。 __get__types.MethodTypefunctools.partial 具有相同的行为。

没有闭包的示例:

def handler(self, *args, **kwargs):
    return self


self = True
bound_handler = lambda *args, **kwargs: handler(self, *args, **kwargs)
bound_handler()  # returns True
self = False
bound_handler()  # returns False
handler = print
bound_handler()  # prints False

lambda 表达式是开放的,因此它的自由变量 handlerself 可以被重新绑定。

To expand on @Keith Pinson’s answer which uses a closure and @brian-brazil’s answer which does not, here is why the former is correct.

Example with a closure:

def handler(self, *args, **kwargs):
    return self


self = True
bound_handler = (lambda handler, self:
    lambda *args, **kwargs: handler(self, *args, **kwargs)
)(handler, self)
bound_handler()  # returns True
self = False
bound_handler()  # returns True
handler = print
bound_handler()  # returns True

The outer lambda expression is closed so its bound variables handler and self cannot be rebound. __get__, types.MethodType, and functools.partial have the same behaviour.

Example without a closure:

def handler(self, *args, **kwargs):
    return self


self = True
bound_handler = lambda *args, **kwargs: handler(self, *args, **kwargs)
bound_handler()  # returns True
self = False
bound_handler()  # returns False
handler = print
bound_handler()  # prints False

The lambda expression is open so its free variables handler and self can be rebound.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文