본문 바로가기
Python Basic

파이썬의 dis 모듈

by mjk0618 2023. 10. 16.

최근 파이썬 3.12 버전이 공개되면서 몇 가지 변경점이 소개되었습니다. 자세한 내용은 공식 문서를 통해 확인할 수 있고, Real Python에서 각각에 대해서 조금 더 이해하기 쉽게 풀이한 내용을 볼 수 있습니다. 해당 글에는 Faster Python: More Specializations and Inline Comprehensions라는 섹션이 있습니다. 거기에 dis라는 모듈이 언급되는데 그 역할이 흥미로워서 간단하게 정리해보았습니다.

 

dis는 CPython 바이트코드를 분해하고 분석할 수 있도록 하는 모듈입니다. 이 모듈이 입력받는 CPython 바이트코드는 include/opcode.h 파일에 정의되어 있으며 컴파일러와 인터프리터에서 사용됩니다.

 

 

 

바이트코드는 CPython 인터프리터의 구현 세부 사항입니다. 각 명령어에는 2 바이트가 사용되는데, 이전에는 명령어에 따라 바이트 수가 달랐다고 합니다. 또한 공식 문서에 의하면 버전에 따라 바이트코드가 추가, 제거 또는 변경될 수도 있다고 합니다. 예를 들어 3.10 버전에서는 점프, 예외 처리와 루프 명령어의 인수는 바이트 오프셋(byte offset)에서 명령어 오프셋(instruction offset)으로 변경되었습니다.

 

여기서 바이트 오프셋은 바이트코드 배열의 위치 또는 인덱스를 의미합니다. 명령어 오프셋은 바이트코드 시퀀스에서 명령어의 인덱스를 나타냅니다. 예를 들어 첫 번째 명령어의 길이가 1바이트이고, 두 번째 명령어의 길이가 인수를 포함하여 3바이트라고 하겠습니다. 이 경우 세 번째 명령어의 바이트 오프셋은 4가 되지만, 명령어 오프셋은 2입니다.

 

예시 코드와 함께 dis 모듈을 사용하는 방법을 알아보겠습니다. 다음은 예시 코드와 실행 결과입니다.

 

import dis

def myfunc(alist):
    return len(alist)

>>> dis.dis(myfunc)

4           0 LOAD_GLOBAL              0 (len)
            2 LOAD_FAST                0 (alist)
            4 CALL_FUNCTION            1
            6 RETURN_VALUE

 

출력에서 다섯 개의 열은 순서대로 줄 번호(line number), 바이트 주소(byte address), 명령어 이름(operation code name), 명령어 인수(operation parameters), 인수에 대한 설명(interpretation of the parameters)입니다.

 

Bytecode analysis API는 파이썬 코드를 Bytecode 객체로 래핑하여 컴파일된 코드에 대한 세부 사항을 제공합니다. 예를 들어 다음과 같이 사용할 수 있습니다.

 

>>> bytecode = dis.Bytecode(myfunc)
>>> for instr in bytecode:
>>>     print(instr.opname)

RESUME
LOAD_GLOBAL
LOAD_FAST
CALL
RETURN_VALUE

 

앞에서 사용한 dis 함수는 인수로 받은 객체를 분해합니다. 인수는 모듈, 클래스, 메서드, 함수, 제너레이터, 코루틴, 코드 객체, 소스 코드 또는 원시 바이트 코드의 바이트 시퀀스 등이 될 수 있습니다. 모듈은 모든 함수를 분해하고 클래스는 모든 메서드를 분해합니다. 코드 객체는 바이트 코드 명령어 당 한 줄을 출력합니다. 중첩된 코드일 경우 재귀적으로 분해합니다.

 

모듈과 관련하여 다양한 함수와 메서드가 존재하고 명령어 집합에 대해서도 많은 내용이 있는데 모두 생소하고 잘 사용하지 않는 개념이라 전부 담지는 못했습니다. 다만 이런 기능을 하는 파이썬 모듈이 있다는 사실 자체가 흥미로웠습니다. 모듈의 기능과 역할에 대해서 간단하게 정리해보았는데, 추후 실용적으로 사용할 수 있는 방법이나 응용 사례가 있다면 추가로 정리해보겠습니다.

댓글