Python 표준 라이브러리의 dis 모듈은 파이썬 바이트코드를 사람이 읽기 쉬운 형태로 역어셈블(disassemble)하여 분석할 수 있도록 도와주는 다양한 함수를 제공합니다. 이를 활용하면 코드의 동작 원리를 깊이 이해하고 성능 최적화 작업을 수행할 수 있습니다. 다만 바이트코드는 인터프리터의 버전별 구현 세부 사항이므로, Python 버전에 따라 출력 결과가 달라질 수 있다는 점에 유의해야 합니다.
dis() 함수
dis() 함수는 모듈, 클래스, 메서드, 함수 또는 코드 객체 등 어떤 종류의 Python 코드 소스든 역어셈블된 표현을 생성합니다.
>>> def hello():
print("hello world")
>>> import dis
>>> dis.dis(hello)
2 0 LOAD_GLOBAL 0 (print)
3 LOAD_CONST 1 ('hello world')
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
Bytecode() 클래스
바이트코드 분석 API는 Bytecode 클래스에 정의되어 있습니다. 생성자는 Bytecode 객체를 반환하며, 이 객체는 바이트코드를 분석할 수 있는 여러 메서드를 제공합니다.
Bytecode()는 생성자로서, 함수, 제너레이터, 메서드, 소스 코드 문자열 또는 코드 객체에 해당하는 바이트코드를 분석합니다. 여러 내부 함수들을 편리하게 감싸는 래퍼(wrapper) 역할을 합니다.
>>> string = dis.Bytecode(hello)
>>> for x in string:
print(x)
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=0, argval='print', argrepr='print', offset=0, starts_line=2, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='hello world', argrepr="'hello world'", offset=3, starts_line=None, is_jump_target=False)
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='1 positional, 0 keyword pair', offset=6, starts_line=None, is_jump_target=False)
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=9, starts_line=None, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=10, starts_line=None, is_jump_target=False)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=13, starts_line=None, is_jump_target=False)
code_info() 함수
code_info() 함수는 Python 코드 객체에 대한 상세 정보를 문자열 형태로 반환합니다.
>>> dis.code_info(hello) "Name: hello\nFilename: <pyshell#2>\nArgument count: 0\nKw-only arguments: 0\nNumber of locals: 0\nStack size: 2\nFlags: OPTIMIZED, NEWLOCALS, NOFREE\nConstants:\n 0: None\n 1: 'hello world'\nNames:\n 0: print"
show_code() 함수
show_code() 함수는 Python 모듈, 함수 또는 클래스의 상세한 코드 정보를 화면에 출력합니다.
>>> dis.show_code(hello)
Name: hello
Filename: <pyshell#2>
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 'hello world'
Names:
0: print
disassemble() 함수
disassemble() 함수는 코드 객체를 역어셈블하여 다음과 같은 열(column)로 구분된 결과를 출력합니다.
- 각 줄의 첫 번째 명령어에 해당하는 줄 번호(line number)
- 현재 실행 중인 명령어를 나타내는
-->표시 - 분기 대상인 명령어를 나타내는
>>레이블 표시 - 명령어의 주소(offset)
- 연산 코드(opcode) 이름
- 연산 매개변수
- 괄호 안에 표시되는 매개변수의 해석
>>> codeInString = 'a = 5\nb = 6\nsum = a + b \nprint("sum = ", sum)'
>>> codeObject = compile(codeInString, 'sumstring', 'exec')
>>> dis.disassemble(codeObject)
출력 결과
1 0 LOAD_CONST 0 (5)
3 STORE_NAME 0 (a)
2 6 LOAD_CONST 1 (6)
9 STORE_NAME 1 (b)
3 12 LOAD_NAME 0 (a)
15 LOAD_NAME 1 (b)
18 BINARY_ADD
19 STORE_NAME 2 (sum)
4 22 LOAD_NAME 3 (print)
25 LOAD_CONST 2 ('sum =')
28 LOAD_NAME 2 (sum)
31 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
34 POP_TOP
35 LOAD_CONST 3 (None)
38 RETURN_VALUE
get_instructions() 함수
get_instructions() 함수는 지정된 함수, 메서드, 소스 코드 문자열 또는 코드 객체에 포함된 명령어들에 대한 반복자(iterator)를 반환합니다. 이 반복자는 코드 내 각 연산의 세부 정보를 담고 있는 Instruction 네임드 튜플(named tuple) 시리즈를 생성합니다.
>>> it = dis.get_instructions(code)
>>> for i in it:
print(i)
Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=<code object hello at 0x02A9BA70, file "<disassembly>", line 2>, argrepr='<code object hello at 0x02A9BA70, file "<disassembly>", line 2>', offset=0, starts_line=2, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval='hello', argrepr="'hello'", offset=3, starts_line=None, is_jump_target=False)
Instruction(opname='MAKE_FUNCTION', opcode=132, arg=0, argval=0, argrepr='', offset=6, starts_line=None, is_jump_target=False)
Instruction(opname='STORE_NAME', opcode=90, arg=0, argval='hello', argrepr='hello', offset=9, starts_line=None, is_jump_target=False)
Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=None, argrepr='None', offset=12, starts_line=None, is_jump_target=False)
Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=15, starts_line=None, is_jump_target=False)
각 명령어 정보는 아래 표와 같은 필드를 가진 튜플 형태의 객체로 제공됩니다.
| 필드명 | 설명 |
|---|---|
| opcode | 연산에 해당하는 숫자 코드로, opcode 목록 및 Opcode 컬렉션의 바이트코드 값과 일치합니다. |
| opname | 연산의 사람이 읽을 수 있는 이름입니다. |
| arg | 연산의 숫자 인자(있는 경우). 없으면 None입니다. |
| argval | 해석된 실제 인자 값(알려진 경우). 알 수 없으면 arg와 동일한 값입니다. |
| argrepr | 연산 인자에 대한 사람이 읽을 수 있는 설명입니다. |
| offset | 바이트코드 시퀀스 내에서 해당 연산이 시작되는 인덱스입니다. |
| starts_line | 이 opcode가 시작하는 줄 번호(있는 경우). 없으면 None입니다. |
| is_jump_target | 다른 코드가 이 위치로 점프(jump)하면 True, 그렇지 않으면 False입니다. |