Wo sind die Betreiber auf Magie-Methoden in Python?

Ich habe über Magie-Methoden in Python gelesen, und ich habe eine Menge von Informationen über die Überschreitung gefunden und welchen Zweck sie dienen, aber ich habe nicht in der Lage zu finden, wo in der sprachspezifischen Operatoren und Aktionen sind auf diejenigen abgebildet Methoden ( + sucht nach __add__ , += sucht nach __iadd__ , das Erstellen eines neuen Objekts aus einer Klasse könnte __new__ und __init__ usw. __init__ . Gibt es irgendwo kann ich sehen, was passiert, wenn der Python-Interpreter (oder was auch immer niedrigerer Level-Mechanismus) auftritt Pluszeichen?

5 Solutions collect form web for “Wo sind die Betreiber auf Magie-Methoden in Python?”

Ihre Frage ist ein bisschen generisch. Es gibt eine umfassende Liste von "speziellen Methoden", obwohl es einige stdlib spezifische Methoden verpasst (zB __setstate__ und __getstate__ die von __setstate__ __getstate__ verwendet werden. Aber es ist ein Protokoll des Modul pickle kein Sprachprotokoll).

Wenn Sie genau wissen wollen, was der Dolmetscher ist, können Sie das Modul verwenden, um den Bytecode zu zerlegen:

 >>> import dis >>> def my_func(a): ... return a + 2 ... >>> dis.dis(my_func) 2 0 LOAD_FAST 0 (a) 3 LOAD_CONST 1 (2) 6 BINARY_ADD 7 RETURN_VALUE 

Sie können sehen, dass der intereper einen BINARY_ADD Bytecode ausführt, wenn er zusätzlich BINARY_ADD . Wenn du genau die Operationen sehen BINARY_ADD , die BINARY_ADD kannst du Pythons Quellcode herunterladen und die Datei ceval.c überprüfen:

  case BINARY_ADD: w = POP(); v = TOP(); if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) { /* INLINE: int + int */ register long a, b, i; a = PyInt_AS_LONG(v); b = PyInt_AS_LONG(w); /* cast to avoid undefined behaviour on overflow */ i = (long)((unsigned long)a + b); if ((i^a) < 0 && (i^b) < 0) goto slow_add; x = PyInt_FromLong(i); } else if (PyString_CheckExact(v) && PyString_CheckExact(w)) { x = string_concatenate(v, w, f, next_instr); /* string_concatenate consumed the ref to v */ goto skip_decref_vx; } else { slow_add: x = PyNumber_Add(v, w); } Py_DECREF(v); skip_decref_vx: Py_DECREF(w); SET_TOP(x); if (x != NULL) continue; break; 

Also hier können wir sehen, dass python special cases int und string ergänzt, und schließlich fällt zurück zu PyNumber_Add , die prüft, ob der erste operand __add__ implementiert und ruft es, schließlich versucht es __radd__ von der rechten seite und wenn nichts funktioniert hebt ein TypeError .

Beachten Sie, dass die Byte-Codes versionsspezifisch sind, also wird dies unterschiedliche Ergebnisse bei verschiedenen Versionen zeigen:

 # python2.7 >>> def my_func(): ... return map((lambda x: x+1), range(5)) ... >>> dis.dis(my_func) 2 0 LOAD_GLOBAL 0 (map) 3 LOAD_CONST 1 (<code object <lambda> at 0x16f8c30, file "<stdin>", line 2>) 6 MAKE_FUNCTION 0 9 LOAD_GLOBAL 1 (range) 12 LOAD_CONST 2 (5) 15 CALL_FUNCTION 1 18 CALL_FUNCTION 2 21 RETURN_VALUE # python3 >>> dis.dis(my_func) 2 0 LOAD_GLOBAL 0 (map) 3 LOAD_CONST 1 (<code object <lambda> at 0x7f1161a76930, file "<stdin>", line 2>) 6 LOAD_CONST 2 ('my_func.<locals>.<lambda>') 9 MAKE_FUNCTION 0 12 LOAD_GLOBAL 1 (range) 15 LOAD_CONST 3 (5) 18 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 21 CALL_FUNCTION 2 (2 positional, 0 keyword pair) 24 RETURN_VALUE 

Auch der gleiche Byte-Code kann in zukünftigen Versionen optimiert werden, also auch wenn der Byte-Code die gleichen verschiedenen Versionen von Python ist, wird tatsächlich verschiedene Anweisungen ausführen.

Wenn Sie daran interessiert sind zu lernen, wie Python hinter den Kulissen funktioniert, würde ich Ihnen raten, einige C-Erweiterungen zu schreiben, nach den Tutorials und der Dokumentation, die Sie auf der Website des offiziellen Pythons finden können.

dis Modul kann dir dabei etwas helfen:

Lassen Sie uns ein Beispiel für eine einfache Liste:

 In [12]: def func(): lis=[1,2,3] for i in range(5): lis+=[i] ....: In [13]: def func1(): lis=[1,2,3] for i in range(5): lis =lis + [i] ....: In [14]: dis.dis(func) 2 0 LOAD_CONST 1 (1) 3 LOAD_CONST 2 (2) 6 LOAD_CONST 3 (3) #removed some lines of code 4 34 LOAD_FAST 0 (lis) 37 LOAD_FAST 1 (i) 40 BUILD_LIST 1 43 INPLACE_ADD # += means inplace add is used # ie `__iadd()__` 44 STORE_FAST 0 (lis) 47 JUMP_ABSOLUTE 28 >> 50 POP_BLOCK >> 51 LOAD_CONST 0 (None) 54 RETURN_VALUE In [15]: dis.dis(func1) 2 0 LOAD_CONST 1 (1) 3 LOAD_CONST 2 (2) 6 LOAD_CONST 3 (3) 9 BUILD_LIST 3 12 STORE_FAST 0 (lis) #removed some lines of code 4 34 LOAD_FAST 0 (lis) 37 LOAD_FAST 1 (i) 40 BUILD_LIST 1 43 BINARY_ADD #normal binary add was used #ie __add__ 44 STORE_FAST 0 (lis) 47 JUMP_ABSOLUTE 28 >> 50 POP_BLOCK >> 51 LOAD_CONST 0 (None) 54 RETURN_VALUE 

Es ist nicht trivial, den einzelnen Platz in CPython-Quellen __add__ , die den Operator + auf die spezielle Methode __add__ wegen der Ebenen der Abstraktion beteiligen.

Wie anders geantwortet, wird + mit dem Operanden BINARY_ADD implementiert, der BINARY_ADD PyNumber_Add (mit Ausnahme einiger speziell optimierter Fälle). PyNumber_Add hingegen betrachtet das tp_as_number des tp_as_number , um zum PyNumberMethods Struktur zu gelangen, dessen nb_add Member auf die C-Funktion hinweist, die die Addition implementiert.

Dies ist alles klar für eingebaute Typen, die ihre eigenen nb_add definieren , aber wie funktioniert Ihr __add__ , definiert in Python, übersetzen in einen Funktionszeiger in nb_add gespeichert? Dieser Teil wird von typeobject.c : Wenn Sie eine Klasse definieren, die __add__ implementiert, wird die Maschinerie in typeobject.c in object->type->tp_as_number->nb_add eine generische Funktion, die __add__ auf das Objekt __add__ und aufruft Implementieren die Addition. Für den Fall von __add__ wird diese generische Funktion slot_nb_add und wird mit dem Makro slot_nb_add definiert .

Wie für __new__ und __init__ , werden sie vom __call__ Operator des type Objekts selbst ( tp_call in CPython-Implementierung lingo) tp_call . Das ist nur logisch, denn in Python rufst du den Typ auf, um ein Objekt zu konstruieren.

http://docs.python.org/2/library/dis.html

 class x: def __add__(self,other): return "asd" def test(): return x() + "aaaa" import dis dis.dis(test) 

Was so etwas zurückgibt

  2 0 LOAD_GLOBAL 0 (x) 3 CALL_FUNCTION 0 6 LOAD_CONST 1 ('aaaa') 9 BINARY_ADD 10 RETURN_VALUE 

Das ist der nächste, den du zu "niedrigem Niveau" kommst

Vielleicht möchten Sie diesen Teil der Dokumentation ausprobieren:

http://docs.python.org/3/reference/datamodel.html#special-method-namen

Python ist die beste Programmiersprache der Welt.