18

Python(2.7.2)では、なぜ

import dis
dis.dis("i in (2, 3)")

期待どおりに動作しますが

import dis
dis.dis("i in [2, 3]")

レイズ:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dis.py", line 45, in dis
  disassemble_string(x)
File "/usr/lib/python2.7/dis.py", line 112, in disassemble_string
  labels = findlabels(code)
File "/usr/lib/python2.7/dis.py", line 166, in findlabels
 oparg = ord(code[i]) + ord(code[i+1])*256
IndexError: string index out of range

これはPython3には影響しないことに注意してください。

4

3 に答える 3

29

短い答え

Python 2.xでは、str型は生のバイトを保持するためdis、文字列を渡すと、コンパイルされたバイトコードを取得していると想定します。それはあなたがそれをバイトコードとして渡す文字列を逆アセンブルしようとします、そして-純粋にPythonバイトコードの実装の詳細のために-は成功しi in (2,3)ます。明らかに、しかし、それはぎこちないものを返します。

Python 3.xでは、strタイプは文字列用であり、bytesタイプはrawバイト用であるためdis、コンパイルされたバイトコードと文字列を区別できます。文字列を取得すると、ソースコードを取得していると見なされます。


長い答え

これが私がこれを解決するために従った思考プロセスです。

  1. Python(3.2)で試してみました:

    >>> import dis
    >>> dis.dis("i in (2,3)")  
      1           0 LOAD_NAME                0 (i)
                  3 LOAD_CONST               2 ((2, 3))
                  6 COMPARE_OP               6 (in)
                  9 RETURN_VALUE
    >>> dis.dis("i in [2,3]")
      1           0 LOAD_NAME                0 (i)
                  3 LOAD_CONST               2 ((2, 3))
                  6 COMPARE_OP               6 (in)
                  9 RETURN_VALUE
    

    明らかに、これは機能します。

  2. Python2.7で試してみました。

    >>> import dis
    >>> dis.dis("i in (2,3)")
              0 BUILD_MAP       26912
              3 JUMP_FORWARD    10272 (to 10278)
              6 DELETE_SLICE+0
              7 <44>
              8 DELETE_SLICE+1
              9 STORE_SLICE+1
    >>> dis.dis("i in [2,3]")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:\Python27\lib\dis.py", line 45, in dis
        disassemble_string(x)
      File "C:\Python27\lib\dis.py", line 112, in disassemble_string
        labels = findlabels(code)
      File "C:\Python27\lib\dis.py", line 166, in findlabels
        oparg = ord(code[i]) + ord(code[i+1])*256
    IndexError: string index out of range
    

    あはは!また、Python 3.2で生成されたバイトコードは期待どおり(「ロードi、ロード(2,3)、メンバーシップのテスト、結果を返す」)であるのに対し、Python2.7で取得したものはぎこちないものであることに注意してください。明らかに、dis2.7では文字列をバイトコードとして逆コンパイルしていますが、3.2ではPythonとしてコンパイルしています。

  3. のソースコードを調べましたdis.dis。重要なポイントは次のとおりです。

    Python 2.7:

    elif isinstance(x, str):
        disassemble_string(x)
    

    Python 3.2:

       elif isinstance(x, (bytes, bytearray)): # Raw bytecode
           _disassemble_bytes(x)
       elif isinstance(x, str):    # Source code
           _disassemble_str(x)
    

    楽しみのために、Python3で同じバイトを渡すことでこれを確認しましょうdis

    >>> dis.dis("i in (2,3)".encode())
              0 BUILD_MAP       26912
              3 JUMP_FORWARD    10272 (to 10278)
              6 <50>
              7 <44>
              8 <51>
              9 <41>
    

    あはは!ジブリッシュ!(ただし、それはわずかに異なるジブリッシュであることに注意してください-バイトコードはPythonバージョンで変更されています!)

于 2012-05-06T20:39:00.140 に答える
18

dis.disPythonソースコードではなく、引数としてバイトコードを想定しています。最初の例は「機能」しますが、意味のある出力は提供されません。あなたはおそらく欲しい:

import compiler, dis

code = compiler.compile("i in [2, 3]", '', 'single')
dis.dis(code)

これは期待どおりに機能します。(私は2.7でのみテストしました)。

于 2012-05-06T20:21:29.370 に答える
9

単純な式のバイトコードを取得しようとしている場合は、ラムダの本体として式を使用してラムダとしてdisに渡すのが最も簡単です。

>>> import dis
>>> dis.dis(lambda i : i in [3,2])
  1           0 LOAD_FAST                0 (i)
              3 LOAD_CONST               2 ((3, 2))
              6 COMPARE_OP               6 (in)
              9 RETURN_VALUE
于 2012-05-06T21:03:23.587 に答える