Python

Pythonで関数をdisする

##加筆途中##

概要

Pythonでdisモジュールを用いて、関数の内部構造を確認する。

目的

  • Python3の内部動作を学ぶ準備として、関数の内部構造を確認する。

環境

os: maxOS Sierra Version 10.12.6
python: Python 3.6.0

内容

インタープリタ
>>> def f(a, b):
...     c = len(a)
...     d = list(range(3))
...     a + b
...
>>> import dis
>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (len)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 STORE_FAST               2 (c)

  3           8 LOAD_GLOBAL              1 (list)
             10 LOAD_GLOBAL              2 (range)
             12 LOAD_CONST               1 (3)
             14 CALL_FUNCTION            1
             16 CALL_FUNCTION            1
             18 STORE_FAST               3 (d)

  4          20 LOAD_FAST                0 (a)
             22 LOAD_FAST                1 (b)
             24 BINARY_ADD
             26 POP_TOP
             28 LOAD_CONST               0 (None)
             30 RETURN_VALUE

詳細

disする関数の定義

以下のように、(深い意味はない)関数fを定義した。

インタープリタ
>>> def f(a, b):
...     c = len(a)
...     d = list(range(3))
...     a + b
...

dis.disの実行

インタープリタ
>>> import dis
>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (len)
              2 LOAD_FAST                0 (a)
              4 CALL_FUNCTION            1
              6 STORE_FAST               2 (c)

  3           8 LOAD_GLOBAL              1 (list)
             10 LOAD_GLOBAL              2 (range)
             12 LOAD_CONST               1 (3)
             14 CALL_FUNCTION            1
             16 CALL_FUNCTION            1
             18 STORE_FAST               3 (d)

  4          20 LOAD_FAST                0 (a)
             22 LOAD_FAST                1 (b)
             24 BINARY_ADD
             26 POP_TOP
             28 LOAD_CONST               0 (None)
             30 RETURN_VALUE

1列目は元のファイルでの行数を、2列目は何バイト目のコードであるかを、3列目は人間が読むための命令名を、

各バイトコード命令の確認

co_varnames, co_names, co_constsの確認

>>> for attr in ('co_varnames', 'co_names', 'co_consts'):
...     print('{0}:\t{1}'.format(attr, getattr(f.__code__, attr)))
...
co_varnames:    ('a', 'b', 'c', 'd')
co_names:       ('len', 'list', 'range')
co_consts:      (None, 3)

予めco_varnames, co_names, co_constsを確認しておいた

1行目

  2           0 LOAD_GLOBAL              0 (len)

(元ファイル2行目)(0バイト目の命令)co_names[0]つまり'len'という名前のグローバルをスタック上にロードする。

LOAD_GLOBAL(namei)
      Loads the global named co_names[namei] onto the stack.
      co_names[namei] という名前のグローバルをスタック上にロードします。

2行目

              2 LOAD_FAST                0 (a)

2バイト目の命令)ローカルなco_varnames[0]つまり'a'への参照をスタックにプッシュする。

LOAD_FAST(var_num)
      Pushes a reference to the local co_varnames[var_num] onto the stack.
      ローカルな co_varnames[var_num] への参照をスタックにプッシュします。

3行目

              4 CALL_FUNCTION            1

CALL_FUNCTION(argc)
      Calls a function. argc indicates the number of positional arguments. The positional arguments are on the stack, with the right-most argument on top. Below the arguments, the function object to call is on the stack. Pops all function arguments, and the function itself off the stack, and pushes the return value.

      Changed in version 3.6: This opcode is used only for calls with positional arguments.

4行目

              6 STORE_FAST               2 (c)

STORE_FAST(var_num)
      Stores TOS into the local co_varnames[var_num].
      TOS をローカルな co_varnames[var_num] の中に保存します。

5行目

  3           8 LOAD_GLOBAL              1 (list)

LOAD_GLOBAL(namei)
      Loads the global named co_names[namei] onto the stack.
      co_names[namei] という名前のグローバルをスタック上にロードします。

6行目

             10 LOAD_GLOBAL              2 (range)

LOAD_GLOBAL(namei)
      Loads the global named co_names[namei] onto the stack.
      co_names[namei] という名前のグローバルをスタック上にロードします。

7行目

             12 LOAD_CONST               1 (3)

LOAD_CONST(consti)
      Pushes co_consts[consti] onto the stack.
      co_consts[consti] をスタックにプッシュします。

8行目

             14 CALL_FUNCTION            1

CALL_FUNCTION(argc)
      Calls a function. argc indicates the number of positional arguments. The positional arguments are on the stack, with the right-most argument on top. Below the arguments, the function object to call is on the stack. Pops all function arguments, and the function itself off the stack, and pushes the return value.

      Changed in version 3.6: This opcode is used only for calls with positional arguments.

9行目

             16 CALL_FUNCTION            1

CALL_FUNCTION(argc)
      Calls a function. argc indicates the number of positional arguments. The positional arguments are on the stack, with the right-most argument on top. Below the arguments, the function object to call is on the stack. Pops all function arguments, and the function itself off the stack, and pushes the return value.

      Changed in version 3.6: This opcode is used only for calls with positional arguments.

10行目

             18 STORE_FAST               3 (d)

STORE_FAST(var_num)
      Stores TOS into the local co_varnames[var_num].
      TOS をローカルな co_varnames[var_num] の中に保存します。

11行目

  4          20 LOAD_FAST                0 (a)

LOAD_FAST(var_num)
      Pushes a reference to the local co_varnames[var_num] onto the stack.
      ローカルな co_varnames[var_num] への参照をスタックにプッシュします。

12行目

             22 LOAD_FAST                1 (b)

LOAD_FAST(var_num)
      Pushes a reference to the local co_varnames[var_num] onto the stack.
      ローカルな co_varnames[var_num] への参照をスタックにプッシュします。

13行目

             24 BINARY_ADD

BINARY_ADD
      Implements TOS = TOS1 + TOS.
      TOS = TOS1 + TOS を実行します。

14行目

             26 POP_TOP

POP_TOP
      Removes the top-of-stack (TOS) item.
      スタックの先頭 (TOS) の要素を取り除きます。

15行目

             28 LOAD_CONST               0 (None)

LOAD_CONST(consti)
      Pushes co_consts[consti] onto the stack.
      co_consts[consti] をスタックにプッシュします。

4行目

             30 RETURN_VALUE

RETURN_VALUE
      Returns with TOS to the caller of the function.
      関数の呼び出し元へ TOS を返します。

意義

疑問

どうしてTOSの説明が日本語訳では書いていないのであろうか。

参考文献

[1] 32.12. dis — Disassembler for Python bytecode (https://docs.python.org/3/library/dis.html)
[2] 32.12. dis — Python バイトコードの逆アセンブラ (https://docs.python.org/ja/3/library/dis.html)