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

Есть ли в Python способ связать несвязанный метод, не вызывая его?

Я пишу программу wxPython, и для определенного класса я решил, что было бы неплохо сгруппировать данные всех моих кнопок в один список кортежей на уровне класса, например:

<code>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)
</code>

Проблема в том, что все значенияhandler это несвязанные методы, моя программа взрывается захватывающим пламенем, и я плачу.

Я искал в Интернете решение, которое, по-видимому, должно быть относительно простой и разрешимой. К сожалению, я ничего не смог найти. Прямо сейчас я используюfunctools.partial чтобы обойти это, но знает ли кто-нибудь, существует ли чистый, здоровый, Pythonic способ связать несвязанный метод с экземпляром и продолжить передавать его, не вызывая его?

 jspencer27 июн. 2018 г., 21:57
Мне особенно нравится «эффектное пламя, и я плачу».
 Aiden Bell18 июн. 2009 г., 23:42
@Christopher - метод, который не связан с областью объекта, из которого он был извлечен, поэтому вы должны явно передать себя.
 Christopher18 июн. 2009 г., 23:38
Определить «несвязанный метод»

Ответы на вопрос(5)

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>>
 23 дек. 2011 г., 14:30
+1 Это потрясающе, но в документации по питону по указанному вами URL нет ссылки на него.
 08 авг. 2018 г., 13:27
Это на самом деле упоминается в документации, но на странице дескриптора из другого ответа:docs.python.org/3/howto/descriptor.html#functions-and-methods
 08 окт. 2014 г., 14:48
+1, я предпочитаю, чтобы в моем коде не было вызовов магических функций (т.е.__get__). Я не знаю, для какой версии Python, на которой вы это тестировали, но на Python 3.4MethodType Функция принимает два аргумента. Функция и экземпляр. Так что это должно быть изменено наtypes.MethodType(f, C()).
 31 июл. 2015 г., 09:35
Вот! Это хороший способ исправления методов экземпляра:wgt.flush = types.MethodType(lambda self: None, wgt)

но это альтернативный способ решения той же (или очень похожей) основной проблемы. Вот тривиальный пример:

self.method = (lambda self: lambda args: self.do(args))(self)
 Dan Passaro27 июл. 2018 г., 23:41
Да, это примерно то же самое, что и мое оригинальное исправление, которое должно было использоватьfunctools.partial(handler, self)

у меня есть метод класса и экземпляр, и я хочу применить экземпляр к методу.

Рискуя упрощением вопроса ОП, я закончил тем, что сделал что-то менее загадочное, что может быть полезно для других, кто прибывает сюда (предостережение: я работаю в 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
 Dan Passaro22 июл. 2018 г., 19:27
Это не решает мою проблему - то, что я хотелmeth быть вызванным без необходимости отправлятьa аргумент (именно поэтому я изначально использовалfunctools.partial), но это предпочтительнее, если вам не нужно передавать метод и просто вызывать его на месте. Также это работает так же в Python 2, как и в Python 3.
 23 июл. 2018 г., 00:48
Извиняюсь за то, что не прочитал ваши первоначальные требования более тщательно. Я неравнодушен (каламбур) к подходу, основанному на лямбда-выражениях @ brian-brazil вstackoverflow.com/a/1015355/558639 - он настолько чист, насколько вы можете его получить.

self вhandler:

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

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

 06 мар. 2011 г., 15:15
@ Алан: в чем разница междуfunction чей первый аргумент вы частично иinstancemethod; печатание утки не видит разницы.
 Dan Passaro06 окт. 2015 г., 02:28
@LieRyan разница в том, что вы все еще не имеете дело с фундаментальным типом.functools.partial отбрасывает некоторые метаданные, например__module__, (Также я хочу заявить о записи, которую я очень сильно сжал, когда посмотрел свой первый комментарий к этому ответу.) На самом деле в своем вопросе я упоминаю, что уже используюfunctools.partial но я чувствовал, что должен быть «чище» кстати, так как легко получить как несвязанные, так и связанные методы.
 Dan Passaro18 июн. 2009 г., 23:54
Да, но это вызывает метод. Проблема в том, что мне нужно передать связанный метод как вызываемый объект. У меня есть несвязанный метод и экземпляр, к которому я хотел бы привязаться, но не могу понять, как собрать все это вместе, не вызывая его немедленно
 23 февр. 2011 г., 21:20
Вы могли бы на самом деле использоватьfunctools.partial вместо определения лямбды. Однако это не решает точную проблему. Вы все еще имеете дело сfunction вместоinstancemethod.
 18 июн. 2009 г., 23:55
Нет, он не вызовет метод, только если вы выполняете bound_handler (). Определение лямбда не вызывает лямбда.
Решение Вопроса

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

bound_handler = handler.__get__(self, MyWidget)

Здесь R. Hettinger's отличноруководство дескрипторам.

 28 янв. 2014 г., 22:01
Да, вы узнаете что-то новое каждый день. @Kazark В Python 3, по крайней мере, вы также можете пропустить указание типа, так как__get__ будет принимать это неявно из параметра объекта. Я даже не уверен, что его поставка что-то делает, поскольку не имеет значения, какой тип я указываю в качестве второго параметра, независимо от того, какой экземпляр является первым параметром. Такbind = lambda instance, func, asname=None: setattr(instance, asname or func.__name__, func.__get__(instance)) должен сделать трюк, а также. (Хотя я предпочел бы иметьbind может использоваться в качестве декоратора лично, но это другое дело.)
 19 июн. 2009 г., 00:02
Это довольно круто. Мне нравится, как вы можете опустить тип и получить обратно связанный метод? .F & quot; вместо.
 09 дек. 2017 г., 21:08
Вау, никогда не знал, что функции были дескрипторами. Это очень элегантный дизайн, методы - это просто обычные функции в классе.__dict__ и доступ к атрибутам дает вам несвязанные или связанные методы через обычный протокол дескриптора. Я всегда предполагал, что во времяtype.__new__()
 13 авг. 2011 г., 18:53
И, таким образом, функция для привязки функций к экземплярам классов:bind = lambda instance, func, asname: setattr(instance, asname, func.__get__(instance, instance.__class__)) Пример:class A: pass; a = A(); bind(a, bind, 'bind')
 08 мар. 2011 г., 02:27
Мне нравится это решение надMethodType один, потому что он работает так же в py3k, в то время какMethodTypeАргументы были немного изменены.

Ваш ответ на вопрос