2009-06-18 5 views
92

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

+1

Определение «несвязанный метод» – Christopher

+4

@Christopher - метод, который не связан с областью объекта он отсасывается из, так что вы должны передать себя в явном виде. –

ответ

143

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

bound_handler = handler.__get__(self, MyWidget) 

Вот отличный guide к дескрипторы Р. Hettinger в.

+2

Это довольно круто. Мне нравится, как вы можете опустить тип и вместо этого вернуть «связанный метод? .f». – Kiv

+0

Мне нравится это решение над 'MethodType', потому что оно работает одинаково в py3k, а аргументы' MethodType' немного изменены. – bgw

+8

И, таким образом, функция связывания функций с экземплярами класса: 'bind = lambda instance, func, asname: setattr (instance, asname, func .__ get __ (instance, instance .__ class __))' Пример: 'class A: pass; ' ' a = A(); ' ' bind (a, bind, 'bind') ' –

3

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

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

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

+1

Да, но это вызывает метод. Проблема в том, что мне нужно иметь возможность передавать связанный метод как вызываемый объект. У меня есть метод unbound и экземпляр, к которому я хочу привязываться, но не могу понять, как собрать все это вместе, не называя его немедленно. –

+6

Нет, это не так, это вызовет только метод, если вы do bound_handler(). Определение лямбда не вызывает лямбда. –

+0

Вы можете использовать 'functools.partial' вместо определения лямбда. Однако он не решает точной проблемы. Вы по-прежнему имеете дело с функцией 'function' вместо' instancemethod'. –

68

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

+1 Это потрясающе, но ссылки на него в документах python отсутствуют по указанному вами URL-адресу. –

+2

+1, я предпочитаю, чтобы в моем коде не вызывались магические функции (т. Е. '__get__'). Я не знаю, для какой версии python вы это протестировали, но на python 3.4 функция 'MethodType' принимает два аргумента. Функция и экземпляр. Поэтому это должно быть изменено на 'types.MethodType (f, C())'. –

+0

Вот оно! Это хороший способ исправления методов экземпляра: 'wgt.flush = types.MethodType (lambda self: None, wgt)' – Winand

7

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

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