Python: привязать несвязанный метод? - PullRequest
102 голосов
/ 19 июня 2009

В 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 способ связать несвязанный метод с экземпляром и продолжить передавать его, не вызывая его?

Ответы [ 5 ]

154 голосов
/ 19 июня 2009

Все функции также являются дескрипторами , поэтому вы можете связать их, вызвав их метод __get__:

bound_handler = handler.__get__(self, MyWidget)

Вот превосходное руководство Р. Хеттингера к дескрипторам.


В качестве отдельного примера взят из комментария Кита :

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
76 голосов
/ 19 июня 2009

Это можно сделать аккуратно с 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>>
9 голосов
/ 05 сентября 2011

Создание замыкания с self в нем не будет технически связывать функцию, но это альтернативный способ решения той же (или очень похожей) основной проблемы. Вот тривиальный пример:

self.method = (lambda self: lambda args: self.do(args))(self)
6 голосов
/ 19 июня 2009

Это будет связывать self с handler:

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

Это работает путем передачи self в качестве первого аргумента функции. object.function() - это просто синтаксический сахар для function(object).

1 голос
/ 11 июня 2018

Поздно к вечеринке, но я пришел сюда с похожим вопросом: у меня есть метод класса и экземпляр, и я хочу применить экземпляр к методу.

Рискуя упрощением вопроса ОП, я закончил тем, что сделал что-то менее загадочное, что может пригодиться другим, кто прибывает сюда (предостережение: я работаю в 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
...