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
может быть отскок.