ProgramingTip

dis.dis의 출력을 어떻게 이해해야합니까?

bestdevel 2020. 12. 4. 19:51
반응형

dis.dis의 출력을 어떻게 이해해야합니까?


dis (파이썬 바이트 코드의 분해자) 를 사용하는 방법을 이해하고 싶습니다 . 특히 (또는 ) 의 출력을 어떻게 해석해야 우리 합니까?dis.disdis.disassemble

.

다음은 매우 구체적인 예입니다 (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

JUMP_IF_TRUE_OR_POP등이 바이트 코드 명령 이라는 것을 알았습니다 (흥미롭게 BUILD_SET도이 목록에는 있지만 )BUILD_TUPLE . 에있는 숫자 오른쪽는 메모리 할당이고 왼쪽에있는 숫자 고토 숫자라고 생각합니다. 매번 거의 3 씩 증가합니다 (정말 아님).

dis.dis("heapq.nsmallest(d,3)")내부를 내부면 :

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 

은 소스 코드 당신를 포함하는 문자열을 분해하려고하지만하여 해당 지원되지 않는을 구석으로 dis.dis그것 (기능 참조 바이트 코드를 포함 것처럼 문자열 인수와 파이썬 2에서, 문자열을 취급 그것은 disassemble_string에를dis.py ). 따라서 소스 코드를 바이트 코드로 잘못 해석하여 무의미한 출력이 표시됩니다.

파이썬 3에서는 dis.dis문자열 인수 를 분해하기 전에 컴파일하는 상황이 다릅니다 .

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         

Python 2에서는 다음으로 전달하기 전에 코드를 직접 수행해야합니다 dis.dis.

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        

숫자는 무엇을 의미합니까? 1맨 왼쪽이 바이트 코드의 컴파일 소스 코드 라인 번호이다. 숫자는 opargs 입니다. 실제 바이트 코드를 살펴 보겠습니다.

>>> co = compile('heapq.nlargest(d,3)', '<none>', 'eval')
>>> co.co_code.encode('hex')
'6500006a010065020064000083020053'

바이트 코드의 시스템 0에서 oparg 65를 사용하여 opcode를 LOAD_NAME찾습니다 0000. 그런 다음 (오프셋 3에서) 6aopcode LOAD_ATTR, 0100oparg 등이 있습니다. opargs는 리틀 엔디안 순서 0100번호 1입니다. 문서화되지 않은 opcode모듈에는 각 opnameopcode에 대한 이름과 각 이름에 대한 opcode를 제공하는 테이블이 포함되어 있습니다 opmap.

>>> opcode.opname[0x65]
'LOAD_NAME'

oparg의 의미는 오피 코드에 따라 달라지며 전체 이야기에 대한 당신은 CPython에 가상 머신의 구현 읽을 필요 에를ceval.c . For LOAD_NAMELOAD_ATTRoparg는 co_names코드 객체 속성에 대한 인덱스입니다 .

>>> co.co_names
('heapq', 'nlargest', 'd')

들어 LOAD_CONST그것의 인덱스입니다 co_consts코드 개체의 속성 :

>>> co.co_consts
(3,)

의 경우 CALL_FUNCTION하위 바이트의 일반 인수 수와 상위 바이트의 키워드 인수 수를 사용하여 16 비트로 인코딩 된 함수에 전달할 인수의 수입니다.


인터넷 검색 중에 찾을 수 있도록 다른 질문에 대한 답변을 다시 게시 하고 있습니다 dis.dis().


위대한 Gareth Rees의 답변 을 완성 하기 위해 분해 된 바이트 코드의 출력을 설명하는 작은 열 단위 요약이 있습니다.

예를 들어 다음과 같은 함수가 있습니다.

def f(num):
    if num == 42:
        return True
    return False

이것은 (Python 3.6)로 분해 될 수 있습니다 :

(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          |   |

각 열에는 특정 용도가 있습니다.

  1. 소스 코드 의 해당 줄 번호
  2. 선택적으로 실행 현재 명령어를 나타냅니다 ( : 프레임 객체 에서 바이트 코드를 가져온 경우 ).
  3. JUMP이전 지침 에서이 지침 까지 가능한 것을 나타내는 레이블
  4. 바이트 인덱스에 해당하는 바이트 코드 주소 (Python 3.6은 각 명령어에 2 바이트를 사용하기 때문에 2의 배수이지만 이전 버전에서는 다를 수 있음)
  5. 명령어 이름 ( opname 이라고도 함 ) dis모듈에 간략하게 설명되어 있으며 해당 구현은 ceval.c(CPython의 핵심 루프) 에서 찾을 수 있습니다.
  6. 일부 상수 또는 변수를 가져오고, 스택을 관리하고, 특정 명령어로 점프하는 등 Python에서 내부적으로 사용하는 명령어 인수 (있는 경우)입니다.
  7. 인간 친화적 인 해석 지시 인수의

참고 URL : https://stackoverflow.com/questions/12673074/how-should-i-understand-the-output-of-dis-dis

반응형