Wie funktioniert __call__?

8

Pythons magische Methode __call__ wird aufgerufen, wenn Sie versuchen, ein Objekt aufzurufen. Cls()() ist also gleich Cls.__call__(Cls()) .

Funktionen sind erstklassige Objekte in Python, was bedeutet, dass sie nur aufrufbare Objekte sind (mit __call__ ). % Co_de% selbst ist jedoch eine Funktion, also auch __call__ , die wiederum eine eigene __call__ hat, die wiederum eine eigene __call__ hat.

Also ist __call__ gleich Cls.__call__(Cls()) und wieder gleichbedeutend mit Cls.__call__.__call__(Cls()) und so weiter und so fort.

Wie endet diese Endlosschleife? Wie führt Cls.__call__.__call__.__call__(Cls()) den Code tatsächlich aus?

    
Markus Meskanen 30.09.2015, 00:34
quelle

3 Antworten

9

Unter der Haube verwenden alle Aufrufe in Python den gleichen Mechanismus und fast alle kommen in der CPython-Implementierung zur selben C-Funktion. Ob ein Objekt eine Instanz einer Klasse mit einer __call__ -Methode, eine Funktion (selbst ein Objekt) oder ein eingebautes Objekt ist, alle Aufrufe (außer für optimierte Sonderfälle) treffen auf die Funktion PyObject_Call . Diese C-Funktion ruft den Typ des Objekts aus dem ob_type -Feld der PyObject -Struktur des Objekts ab und ruft dann vom Typ (ein anderes PyObject struct) das tp_call -Feld ab, das ein Funktionszeiger ist. Wenn tp_call nicht NULL ist, wird durch das aufgerufen, wobei die args- und kwargs-Strukturen auch an PyObject_Call übergeben wurden.

Wenn eine Klasse eine __call__ -Methode definiert, richtet sie das Feld tp_call entsprechend ein.

Hier ist ein Artikel, der all dies im Detail erklärt: Python-Interna: Wie Rufrufe arbeiten . Es listet und erklärt sogar die gesamte Funktion PyObject_Call , die nicht sehr groß ist. Wenn Sie diese Funktion in ihrem ursprünglichen Habitat sehen möchten, finden Sie sie in Objects / abstract.c im CPython-Repo.

Ebenfalls relevant ist dieser stackoverflow Q & A: Was in Python "abrufbar" ist ? .

    
BrianO 30.09.2015, 01:56
quelle
1

Es gibt keine tatsächliche Endlosschleife, da die Methode __call__ für alle diese Situationen nicht tatsächlich aufgerufen ("aufgerufen") wird. Es wird nur direkt aufgerufen, wenn es einen funktionsähnlichen Aufruf für ein Objekt gibt, das eine __call__ -Methode bereitstellt.

Normale Klasseninstanziierung Cls(...) und regulärer Funktionsaufruf f() sind bekannte Fälle, die direkt behandelt werden. Im Allgemeinen gibt es keinen tatsächlichen Aufruf von __call__() . Es gibt also eine endliche Anzahl von __call__ Methodenaufrufen, die auch in komplexen Fällen mit tiefer Vererbung, Metaklassen usw. auftreten können.

Da es Streit darüber gab, ob das Kurzschließen konzeptueller Endlosschleifen tatsächlich stattfand, schauen wir uns den disassemblierten Bytecode an. Betrachten Sie den folgenden Code:

%Vor%

Tut mir leid, es ist ein wenig komplex, da ich mehrere Kurzschlüsse anzeigen möchte - nämlich normale def -Funktionen, __init__ Aufrufe, normale Methoden und __call__ spezielle Methoden. Jetzt:

Es gibt also eine Reihe von Zeiten, in denen, wenn Python tatsächlich wirklich den Baum der konzeptuellen __call__ -Aufrufe "durchläuft", Function (und möglicherweise Method -Klassen) referenziert und ihre% co_de aufgerufen werden % Methoden). Es tut es nicht. Es verwendet in allen Fällen den einfachen Bytecode __call__ , was den konzeptuellen Baumspaziergang kurzschließt. Logischerweise können Sie sich vorstellen, dass es eine Klasse CALL_FUNCTION gibt, die eine Function -Methode hat, die aufgerufen wird, wenn eine Funktion (d. h. eine Instanz der Klasse __call__ ) aufgerufen wird. Aber so funktioniert das nicht. Der Compiler, der Bytecode-Interpreter und andere Teile der C-Sprachunterstützungen gehen nicht wirklich Metaklassenbäume durch. Sie verkürzen sich wie verrückt.

    
Jonathan Eunice 30.09.2015 00:45
quelle
0

Ich habe keine Dokumentation überprüft, aber von meinen Tests scheint es, dass __call__ nicht immer aufgerufen wird:

%Vor%

druckt

%Vor%     
gre_gor 30.09.2015 00:58
quelle

Tags und Links