Python: привязать несвязанный метод?

Есть ли в 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 ответов

Решение

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

bound_handler = handler.__get__(self, MyWidget)

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

Это можно сделать аккуратно с помощью 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>>

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

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

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

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

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

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

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

Чтобы расширить ответ @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

Внешнее лямбда-выражение закрыто, поэтому его связанные переменные не могут быть перепривязаны.__get__,types.MethodType, иfunctools.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

Лямбда-выражение открыто, поэтому его свободные переменныеhandlerиselfможет быть отскок.

Другие вопросы по тегам