2009-06-18 16 views
92

En Python, ¿hay alguna manera de enlazar un método independiente sin llamarlo?Python: ¿enlazar un método independiente?

Estoy escribiendo un programa wxPython, y para una cierta clase decidí que sería bueno para agrupar los datos de todos mis botones juntos como una lista de nivel de clase de tuplas, así:

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) 

El problema es que, dado que todos los valores de handler son métodos independientes, mi programa explota en un incendio espectacular y lloro.

Estaba buscando en línea una solución a lo que parece ser un problema fácil de resolver. Lamentablemente no pude encontrar nada. En este momento, estoy usando functools.partial para solucionar este problema, pero ¿alguien sabe si hay una forma pitonica limpia, sensata y sana de vincular un método independiente a una instancia y seguir pasándolo sin llamarlo?

+1

definen "método sin consolidar" – Christopher

+4

@Christopher - Un método que no está vinculado al ámbito del objeto al que se aspira desde, por lo que tiene que pasar uno mismo de forma explícita. –

Respuesta

143

Todas las funciones son también descriptores, por lo que puede obligar a ellos llamando a su método __get__:

bound_handler = handler.__get__(self, MyWidget) 

Aquí es excelente guide a descriptores de R. Hettinger.

+2

Eso es genial. Me gusta cómo puedes omitir el tipo y obtener un "método enlazado? .f" en su lugar. – Kiv

+0

Me gusta esta solución sobre la de 'MethodType', porque funciona igual en py3k, mientras que los argumentos de' MethodType' han cambiado un poco. – bgw

+8

Y así, una función para enlazar funciones a instancias de clase: 'bind = instancia lambda, func, asname: setattr (instancia, asname, func .__ get __ (instancia, instancia .__ clase __))' Ejemplo: 'clase A: pase; ' ' a = A(); ' ' bind (a, bind, 'bind') ' –

3

Este se unirá a selfhandler:

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

Esto funciona haciendo pasar self como el primer argumento de la función. object.function() es solo azúcar sintáctico para function(object).

+1

Sí, pero esto llama al método. El problema es que necesito poder pasar el método enlazado como un objeto llamable. Tengo el método independiente y la instancia a la que me gustaría que esté vinculado, pero no puedo entender cómo juntarlo todo sin llamarlo inmediatamente –

+6

No, no lo hace, solo llamará al método si hacer bound_handler(). Definir una lambda no llama a la lambda. –

+0

En realidad podría usar 'functools.partial' en lugar de definir un lambda. Sin embargo, no resuelve el problema exacto. Todavía estás tratando con una 'función' en lugar de un' método de instancia'. –

68

Esto se puede hacer limpiamente con types.MethodType. Ejemplo:

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 Esto es increíble, pero no hay referencias en los documentos Python en la URL que proporcionó. –

+2

+1, prefiero no tener llamadas a funciones mágicas en mi código (es decir, '__get__'). No sé para qué versión de python esto probaste esto, pero en Python 3.4, la función 'MethodType' toma dos argumentos. La función y la instancia. Entonces esto debería cambiarse a 'types.MethodType (f, C())'. –

+0

¡Aquí está! Es una buena manera de parchar los métodos de instancia: 'wgt.flush = types.MethodType (lambda self: None, wgt)' – Winand

7

Crear un cierre con uno mismo en él no vinculará técnicamente la función, pero es una forma alternativa de resolver el mismo problema subyacente (o muy similar). Aquí está un ejemplo trivial:

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