(main) script 부분
Python에서 "main script"는 프로그램 실행을 시작하는 주 진입점이 되는 Python 파일 또는 source code를 의미함.
0 0 RESUME 0
1 2 LOAD_CONST 0 (<code object func at 0x103661890, file "./test.py", line 1>)
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (func)
RESUME 0
: 코드 실행 시작LOAD_CONST 0
: 상수 테이블의 인덱스 0에서 func 함수의 코드 객체를 읽어와 스택에 Push- 이 코드 객체는 컴파일 단계에서 생성되어 상수 테이블에 저장되었음
MAKE_FUNCTION 0
: 스택에서 코드 객체를 Pop하여 호출 가능한 함수 객체 생성 후 스택에 PushSTORE_NAME 0 (func)
: 스택에서 함수 객체를 팝하여 전역 네임스페이스 딕셔너리의 'func' 키에 저장
6 8 LOAD_CONST 1 (10)
10 STORE_NAME 1 (a)
7 12 LOAD_CONST 2 (999999)
14 STORE_NAME 2 (b)
LOAD_CONST 1 (10)
: 상수 테이블의 인덱스 1에서 값 10을 읽어와 스택에 PushSTORE_NAME 1 (a)
: 스택에서 값을 Pop하여 전역 네임스페이스의 'a' 키에 저장LOAD_CONST 2 (999999)
: 상수 테이블의 인덱스 2에서 값 999999를 읽어와 스택에 PushSTORE_NAME 2 (b)
: 스택에서 값을 Pop하여 전역 네임스페이스의 'b' 키에 저장
8 16 PUSH_NULL
18 LOAD_NAME 0 (func)
20 LOAD_NAME 1 (a)
22 LOAD_NAME 2 (b)
24 CALL 2
32 STORE_NAME 3 (c)
PUSH_NULL
: 예외 처리용 null을 스택에 PushLOAD_NAME 0 (func)
: 전역 네임스페이스에서 'func' 키의 값(함수 객체)을 읽어와 스택에 PushLOAD_NAME 1 (a)
: 전역 네임스페이스에서 'a' 키의 값(10)을 읽어와 스택에 PushLOAD_NAME 2 (b)
: 전역 네임스페이스에서 'b' 키의 값(999999)을 읽어와 스택에 PushCALL 2
: 스택에서 함수와 2개의 인자를 Pop하여 함수 호출, 함수 실행 후 결과를 스택에 PushSTORE_NAME 3 (c)
: 스택에서 함수 호출 결과를 Pop하여 전역 네임스페이스의 'c' 키에 저장
10 34 PUSH_NULL
36 LOAD_NAME 4 (print)
38 LOAD_NAME 3 (c)
40 CALL 1
48 POP_TOP
50 RETURN_CONST 3 (None)
PUSH_NULL
: 예외 처리용 null을 스택에 PushLOAD_NAME 4 (print)
: 내장(builtins) 네임스페이스에서 'print' 키의 값(내장 함수)을 읽어와 스택에 PushLOAD_NAME 3 (c)
: 전역 네임스페이스에서 'c' 키의 값을 읽어와 스택에 PushCALL 1
: 스택에서 'print' 함수와 1개의 인자를 Pop하여 함수 호출, 결과(None)를 스택에 PushPOP_TOP
: 스택 맨 위의 값(None
)을 Pop하여 버림RETURN_CONST 3 (None)
: 상수 테이블의 인덱스 3에서None
을 읽어와 반환하고 프로그램 종료
func 함수 부분
1 0 RESUME 0
2 2 LOAD_FAST 0 (a)
4 LOAD_FAST 1 (b)
6 BINARY_OP 0 (+)
10 STORE_FAST 2 (c)
3 12 LOAD_FAST 2 (c)
14 RETURN_VALUE
RESUME 0
: 함수 실행 시작LOAD_FAST 0 (a)
: 지역 변수 배열의 인덱스 0에서 매개변수 'a'의 값을 읽어와 스택에 Push- 함수 호출 시 전달된 인자가 이 배열에 저장됨
LOAD_FAST 1 (b)
: 지역 변수 배열의 인덱스 1에서 매개변수 'b'의 값을 읽어와 스택에 PushBINARY_OP 0 (+)
: 스택에서 두 값을 Pop하여 더한 후 결과를 스택에 Push- 0은 덧셈 연산을 나타내는 오퍼랜드
STORE_FAST 2 (c)
: 스택에서 결과 값을 Pop하여 지역 변수 배열의 인덱스 2('c')에 저장LOAD_FAST 2 (c)
: 지역 변수 배열의 인덱스 2에서 'c'의 값을 읽어와 스택에 PushRETURN_VALUE
: 스택에서 값을 팝하여 함수의 반환 값으로 사용, 호출자의 스택에 Push됨
데이터 저장소 요약
- 상수 테이블(co_consts):
- 컴파일 시점에 결정되는 모든 상수 값(숫자, 문자열, 코드 객체 등)을 저장
LOAD_CONST
명령어가 여기서 값을 읽어옴
- 전역 네임스페이스:
- 모듈 수준의 변수들을 저장하는 딕셔너리
STORE_NAME
이 여기에 값을 저장하고,LOAD_NAME
이 여기서 값을 읽어옴
- 지역 변수 배열:
- 함수 내의 지역 변수와 매개변수를 저장하는 배열
STORE_FAST
가 여기에 값을 저장하고,LOAD_FAST
가 여기서 값을 읽어옴- 인덱스 기반 접근으로 딕셔너리 조회보다 빠름
- 내장 네임스페이스:
- print(), len() 등의 내장 함수를 저장
LOAD_NAME
이 전역 네임스페이스에서 찾지 못한 이름을 여기서 검색
위의 bytescode 결과에 대응하는 Python 코드 소스 파일 test.py는 다음과 같음
def func(a,b):
c = a+b
return c
a = 10
b = 999999
c = func(a,b)
print(c)
위의 결과를 얻어내는 커맨드는 다음임:
❯ python -m dis test.py
또는 소스코드를 사용하는 compile을 이용하는 방식도 있음
#dis_test.py
import dis
with open('./test.py', 'r') as f:
source = f.read()
code_obj = compile(source, "./test.py", "exec")
dis.dis(code_obj)
아니면 __pycache__ 의 .pyc 바이트코드 파일을 직접사용하는 방법도 있음
import dis
import marshal
import importlib.util
import sys
# .pyc 파일 경로
pyc_path = "__pycache__/my_module.cpython-310.pyc" # 실제 파일 경로로 변경
# .pyc 파일의 헤더 크기 (Python 버전에 따라 다름)
# Python 3.7+ 에서는 일반적으로 16바이트
# Magic number (4bytes) + Bit field (4bytes) + Timestamp (4bytes) + Size (4bytes)
HEADER_SIZE = 16
# .pyc 파일 읽기
with open(pyc_path, 'rb') as f:
# 헤더 건너뛰기
f.seek(HEADER_SIZE)
# 바이트코드 로드
code_object = marshal.load(f)
# 바이트코드 디스어셈블
dis.dis(code_object)
'CE' 카테고리의 다른 글
Text File and Binary File: Hex Code (0) | 2025.03.11 |
---|---|
Apple II (1977년, Apple ][ ) (0) | 2025.03.11 |
Switch로 컴퓨터(or 디지털논리회로) 만들기 - Claude Shannon (1937) (0) | 2025.03.11 |
Claude Shannon (0) | 2025.03.11 |
[CE] Differential Analyzer (미분해석기, 1931) (0) | 2025.03.11 |