2009-06-18 9 views
92

In Python esiste un modo per associare un metodo non associato senza chiamarlo?Python: associare un metodo non associato?

Sto scrivendo un programma wxPython, e per una certa classe ho deciso che sarebbe bello raggruppare i dati di tutti i miei pulsanti insieme come una lista a livello di classe di tuple, in questo modo:

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) 

Il problema è che, poiché tutti i valori di handler sono metodi non associati, il mio programma esplode in una fiammata spettacolare e piango.

Cercavo online una soluzione a quello che sembra essere un problema relativamente semplice e risolvibile. Purtroppo non ho trovato nulla. In questo momento, sto usando functools.partial per aggirare questo problema, ma qualcuno sa se esiste un modo pulito, salutare, Python per legare un metodo non associato a un'istanza e continuare a passarlo senza chiamarlo?

+1

Definire "metodo non legato" – Christopher

+4

@Christopher - Un metodo che non è legato al campo di applicazione l'oggetto è stato risucchiato da, quindi bisogna passare di sé in modo esplicito. –

risposta

143

Tutte le funzioni sono anche descrittori, in modo da poterli legare chiamando il loro metodo __get__:

bound_handler = handler.__get__(self, MyWidget) 

Ecco eccellente guide a descrittori di R. Hettinger.

+2

È davvero fantastico. Mi piace come puoi omettere il tipo e recuperare un "metodo associato? .f". – Kiv

+0

Mi piace questa soluzione rispetto a 'MethodType', perché funziona allo stesso modo in py3k, mentre gli argomenti di' MethodType' sono stati modificati un po '. – bgw

+8

E quindi una funzione per associare le funzioni alle istanze di classe: 'bind = istanza lambda, func, asname: setattr (istanza, asname, func .__ get __ (istanza, istanza .__ classe __))' Esempio: 'classe A: passare; ' ' a = A(); ' ' bind (a, bind, 'bind') ' –

3

Questo legherà self-handler:

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

Questo funziona passando self come primo argomento della funzione. object.function() è solo zucchero sintattico per function(object).

+1

Sì, ma questo chiama il metodo. Il problema è che ho bisogno di essere in grado di passare il metodo associato come oggetto callable. Ho il metodo non associato e l'istanza a cui vorrei essere associato, ma non riesco a capire come mettere tutto insieme senza immediatamente chiamarlo –

+6

No, non lo chiamerà, solo chiamerà il metodo se do bound_handler(). Definire una lambda non chiama la lambda. –

+0

Si potrebbe effettivamente usare 'functools.partial' invece di definire un lambda. Tuttavia, non risolve il problema esatto. Hai ancora a che fare con 'function' invece di' instancemethod'. –

68

Questo può essere fatto in modo pulito con types.MethodType. Esempio:

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 Questo è fantastico, ma non c'è alcun riferimento ad esso nei documenti python all'URL che hai fornito. –

+2

+1, preferisco non avere chiamate alle funzioni magiche nel mio codice (ad esempio '__get__'). Non so per quale versione di python è stato testato questo, ma su python 3.4, la funzione 'MethodType' accetta due argomenti. La funzione e l'istanza. Quindi questo dovrebbe essere cambiato in 'types.MethodType (f, C())'. –

+0

Eccolo! È un buon metodo per applicare le patch ai metodi di istanza: 'wgt.flush = types.MethodType (lambda self: None, wgt)' – Winand

7

La creazione di una chiusura con self non vincolerà tecnicamente la funzione, ma è un modo alternativo per risolvere lo stesso (o molto simile) problema sottostante. Ecco un esempio banale:

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