2015-09-30 8 views
8

Il metodo magico di Python __call__ viene chiamato ogni volta che si tenta di chiamare un oggetto. Cls()() è quindi uguale a Cls.__call__(Cls()).In che modo __call__ funziona davvero?

Le funzioni sono oggetti di prima classe in Python, ovvero sono solo oggetti chiamabili (utilizzando __call__). Tuttavia, __call__ è di per sé una funzione, quindi ha anche __call__, che ha ancora il proprio __call__, che ha ancora il proprio __call__.

Dunque Cls.__call__(Cls()) è quindi uguale a Cls.__call__.__call__(Cls()) e nuovamente equivale a Cls.__call__.__call__.__call__(Cls()) e così via e così via.

Come finisce questo ciclo infinito? In che modo __call__ esegue effettivamente il codice?

risposta

9

Sotto il cofano, tutte le chiamate in uso Python lo stesso meccanismo, e quasi tutti arrivano alla stessa funzione C nell'implementazione di CPython.Se un oggetto è un'istanza di una classe con un metodo __call__, una funzione (esso stesso un oggetto) o un oggetto incorporato, tutte le chiamate (ad eccezione dei casi speciali ottimizzati) arrivano alla funzione PyObject_Call. Quella funzione C ottiene il tipo dell'oggetto dal campo ob_type della struttura dell'oggetto PyObject e quindi dal tipo (un'altra struttura PyObject) ottiene il campo tp_call, che è un puntatore a funzione. Se tp_call non è NULL, chiama tramite quello, con le strutture args e kwargs che sono state passate anche a PyObject_Call.

Quando una classe definisce un metodo __call__, imposta il campo tp_call in modo appropriato.

Ecco un articolo che spiega tutto questo in dettaglio: Python internals: How callables work. Elenca e spiega anche l'intera funzione PyObject_Call, che non è molto grande. Se vuoi vedere quella funzione nel suo habitat nativo, è in Objects/abstract.c nel repository CPython.

Rilevante anche questo stackoverflow Q & A: What is a "callable" in Python?.

+0

Ah grazie! Tutto ha senso ora: D –

1

Non esiste un loop infinito reale, perché il metodo __call__ non è effettivamente invocato ("chiamato") per tutte queste situazioni. Viene invocato direttamente solo quando c'è una chiamata simile a una funzione su un oggetto che fornisce un metodo __call__.

L'istanza di classe normale Cls(...) e l'invocazione funzionale regolare f() sono casi noti gestiti direttamente. C'è generalmente non è una chiamata effettiva di __call__(), quindi ci sono un numero finito di __call__ invocazioni di metodi che possono verificarsi mai, anche nei casi complessi con ereditarietà profondo, metaclassi, ecc

Poiché c'era qualche controversia se la il cortocircuito dei loop infiniti concettuali stava davvero accadendo, diamo un'occhiata al bytecode smontato. Si consideri il seguente codice:

def f(x): 
    return x + 1 

class Adder(object): 
    def something(self, x): 
     return x + 19 
    def __call__(self, x): 
     return x + 1 

def lotsacalls(y): 
    u = f(1) 
    a = Adder() 
    z = u + a.something(y) 
    return a(z * 10) 

dispiace è un po 'complessa, come voglio mostrare diversi casi di corto circuito - vale a dire, le normali funzioni def, __init__ chiamate, i metodi normali, e __call__ metodi speciali. Ora:

annotated disassembly

Così qui sono una serie di momenti in cui, se Python erano veramente, veramente "a piedi l'albero" di concettuali __call__ invocazioni, sarebbe fare riferimento Function (e possibilmente Method classi, e richiamare la loro Metodi __call__). Non è così. Utilizza il semplice codice bytecode CALL_FUNCTION in tutti i casi, cortocircuitando l'albero concettuale a piedi in basso. Logicamente si può immaginare che esiste una classe Function che ha un metodo __call__ che viene richiamato quando viene chiamata una funzione (ad esempio un'istanza della classe Function). Ma in realtà non funziona in questo modo. Il compilatore, l'interprete bytecode e altre parti delle sottostringhe in linguaggio C non sono in realtà alberi meta-classe a piedi. Fanno un corto circuito come un matto.

+1

Ma le funzioni stesse sono solo "oggetti che forniscono il metodo' __call__' ", incluso' __call__' stesso. Quindi per invocare '__call__', si chiama' __call__' che chiama di nuovo '__call__'. Da dove viene la linea e su cosa si basa? –

+1

No, in realtà non lo sono. * Concettualmente * possono essere visti come "solo oggetti che forniscono il metodo __call__", ma in realtà sono direttamente compresi dal compilatore e dal runtime e implementati direttamente. È proprio come la Sequenza di Fibonacci, per esempio. Sì, esiste una definizione completamente ricorsiva e una possibile implementazione. Ma la maggior parte delle implementazioni effettive sono iterative. Essi cortocircuitano la ricorsione logica, in modo da non dover faticosamente attraversare lunghe catene di ricorsione. –

+0

Non sto dicendo che ti sbagli, perché non lo so per certo, ma puoi provarlo? Sembrano oggetti normali per me, e la risposta di BrianO con collegamenti al codice sorgente e tutto sembra supportare la mia teoria. Non sto dicendo che ho ragione e che ti sbagli, ma per me al momento sembra che siano solo oggetti ed è l'implementazione C che chiama il '__call__' solo una volta, se ne è stato definito uno. –

0

non ho controllato tutta la documentazione, ma dalle mie prove sembrare __call__ non è sempre chiamato:

def func1(*args, **kargs): 
    print "func1 called", args, kargs 

def func2(*args, **kargs): 
    print "func2 called", args, kargs 

func1.__call__ = func2 

func1() # here is still called func1 

class Cls: 
    def __init__(*args, **kargs): 
     print "init called", args, kargs 
    def __call__(*args, **kargs): 
     print "object called", args, kargs 

obj = Cls() # here is actually called __init__ 
obj() # here is called __call__ 

Questo stampa

func1 called() {} 
init called (<__main__.Cls instance at 0x0000000002A5ED88>,) {} 
object called (<__main__.Cls instance at 0x0000000002A5ED88>,) {} 
+0

Con 'obj = Cls()' si noti che 'Cls' è solo un'istanza di' type', quindi sarebbe uguale a 'type .__ call __()'. (Google: metaclasses). Sebbene la prima scoperta sia interessante, qualcuno sa perché 'func1 .__ call__' non viene richiamato? –

+0

@gre_gor Dimostrazione magnificamente empirica che le chiamate di funzione sono speciali! –

+0

@MarkusMeskanen Sì. 'func1 .__ call__' non viene richiamato perché è un caso speciale. Python non ha bisogno di attraversare il concetto completo di "le funzioni sono solo oggetti", perché questa è una situazione molto comune e ben nota che il complier/runtime può gestire più direttamente. Ci sono ** molte ** istanze di tali scorciatoie nell'implementazione Python. Python mira alla semantica di alto livello, ma spesso salta giù in casi speciali e codice C per le prestazioni. –