Ich würde gerne verstehen, wie man dis (den Dissembler von Python-Bytecode) benutzt . Wie sollte man konkret die Ausgabe von dis.dis
(oder dis.disassemble
) interpretieren ?
.
Hier ist ein sehr spezifisches Beispiel (in Python 2.7.3):
dis.dis("heapq.nsmallest(d,3)")
0 BUILD_SET 24933
3 JUMP_IF_TRUE_OR_POP 11889
6 JUMP_FORWARD 28019 (to 28028)
9 STORE_GLOBAL 27756 (27756)
12 LOAD_NAME 29811 (29811)
15 STORE_SLICE+0
16 LOAD_CONST 13100 (13100)
19 STORE_SLICE+1
Ich sehe, dass JUMP_IF_TRUE_OR_POP
usw. Bytecode-Anweisungen sind (obwohl interessanterweise BUILD_SET
nicht in dieser Liste aufgeführt, obwohl ich davon ausgehe, dass es so funktioniert BUILD_TUPLE
) . Ich denke, die Zahlen auf der rechten Seite sind Speicherzuordnungen, und die Zahlen auf der linken Seite sind Goto- Zahlen ... Ich stelle fest, dass sie sich jedes Mal fast um 3 erhöhen (aber nicht ganz).
Wenn ich mich dis.dis("heapq.nsmallest(d,3)")
in eine Funktion einwickle :
def f_heapq_nsmallest(d,n):
return heapq.nsmallest(d,n)
dis.dis("f_heapq(d,3)")
0 BUILD_TUPLE 26719
3 LOAD_NAME 28769 (28769)
6 JUMP_ABSOLUTE 25640
9 <44> # what is <44> ?
10 DELETE_SLICE+1
11 STORE_SLICE+1
python
python-2.7
Andy Hayden
quelle
quelle
Antworten:
Sie versuchen, eine Zeichenfolge mit Quellcode zu zerlegen, dies wird jedoch
dis.dis
in Python 2 nicht unterstützt . Mit einem Zeichenfolgenargument wird die Zeichenfolge so behandelt, als ob sie Bytecode enthält (siehe Funktiondisassemble_string
indis.py
). Sie sehen also eine unsinnige Ausgabe, die auf einer Fehlinterpretation des Quellcodes als Bytecode basiert.In Python 3 sieht es anders aus, wo
dis.dis
ein String-Argument kompiliert wird, bevor es zerlegt wird:Python 3.2.3 (default, Aug 13 2012, 22:28:10) >>> import dis >>> dis.dis('heapq.nlargest(d,3)') 1 0 LOAD_NAME 0 (heapq) 3 LOAD_ATTR 1 (nlargest) 6 LOAD_NAME 2 (d) 9 LOAD_CONST 0 (3) 12 CALL_FUNCTION 2 15 RETURN_VALUE
In Python 2 müssen Sie den Code selbst kompilieren, bevor Sie ihn an
dis.dis
folgende Adresse übergeben :Python 2.7.3 (default, Aug 13 2012, 18:25:43) >>> import dis >>> dis.dis(compile('heapq.nlargest(d,3)', '<none>', 'eval')) 1 0 LOAD_NAME 0 (heapq) 3 LOAD_ATTR 1 (nlargest) 6 LOAD_NAME 2 (d) 9 LOAD_CONST 0 (3) 12 CALL_FUNCTION 2 15 RETURN_VALUE
Was bedeuten die Zahlen? Die Nummer
1
ganz links ist die Zeilennummer im Quellcode, aus dem dieser Bytecode kompiliert wurde. Die Zahlen in der linken Spalte sind der Versatz der Anweisung innerhalb des Bytecodes, und die Zahlen rechts sind die Opargs . Schauen wir uns den tatsächlichen Bytecode an:>>> co = compile('heapq.nlargest(d,3)', '<none>', 'eval') >>> co.co_code.encode('hex') '6500006a010065020064000083020053'
Bei Offset 0 im Bytecode finden wir
65
den Opcode fürLOAD_NAME
mit dem oparg0000
; dann (bei Offset 3)6a
ist der OpcodeLOAD_ATTR
mit0100
dem Oparg und so weiter. Beachten Sie, dass die Opargs in Little-Endian-Reihenfolge sind, also0100
die Nummer 1. Das undokumentierteopcode
Modul enthält Tabellenopname
, in denen Sie den Namen für jeden Opcode undopmap
den Opcode für jeden Namen angeben:>>> opcode.opname[0x65] 'LOAD_NAME'
Die Bedeutung des Opargs hängt vom Opcode ab. Für die gesamte Story müssen Sie die Implementierung der virtuellen CPython-Maschine in
ceval.c
lesen . FürLOAD_NAME
undLOAD_ATTR
das oparg ist ein Index in derco_names
Eigenschaft des Codeobjekts :>>> co.co_names ('heapq', 'nlargest', 'd')
Denn
LOAD_CONST
es ist ein Index in dieco_consts
Eigenschaft des Codeobjekts:>>> co.co_consts (3,)
Denn
CALL_FUNCTION
es ist die Anzahl der Argumente, die an die Funktion übergeben werden sollen, codiert in 16 Bit mit der Anzahl der normalen Argumente im niedrigen Byte und der Anzahl der Schlüsselwortargumente im hohen Byte.quelle
inspect
Modul dokumentiert . Bytecode-Anweisungen werden mit demdis
Modul dokumentiert . Für die Implementierungsdetails der virtuellen CPython-Maschine müssen Sie den Quellcode einlesenceval.c
.oparg
?NEXTARG
undPEEKARG
Makros inceval.c
.ceval.c
Sie können das Ausführungsmodell docs.python.org/3.3/reference/executionmodel.htmlIch reposte meine Antwort auf eine andere Frage , um sicherzugehen, dass ich sie beim Googeln finde
dis.dis()
.Um die Antwort des großen Gareth Rees zu vervollständigen , finden Sie hier nur eine kleine spaltenweise Zusammenfassung, um die Ausgabe des zerlegten Bytecodes zu erläutern.
Zum Beispiel mit dieser Funktion:
def f(num): if num == 42: return True return False
Dies kann in (Python 3.6) zerlegt werden:
(1)|(2)|(3)|(4)| (5) |(6)| (7) ---|---|---|---|----------------------|---|------- 2| | | 0|LOAD_FAST | 0|(num) |-->| | 2|LOAD_CONST | 1|(42) | | | 4|COMPARE_OP | 2|(==) | | | 6|POP_JUMP_IF_FALSE | 12| | | | | | | 3| | | 8|LOAD_CONST | 2|(True) | | | 10|RETURN_VALUE | | | | | | | | 4| |>> | 12|LOAD_CONST | 3|(False) | | | 14|RETURN_VALUE | |
Jede Spalte hat einen bestimmten Zweck:
JUMP
von einer früheren Anweisung zu dieser bezeichnetdis
Modul und deren Umsetzung finden sich inceval.c
(der Kernschleife CPython)quelle