【问题标题】:Why does Python's dis dislike lists?为什么 Python 的不喜欢列表?
【发布时间】:2012-05-06 19:53:21
【问题描述】:

在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。

【问题讨论】:

  • 但是dis.dis("a = [2, 3]; i in a") 工作正常
  • 它适用于 python 3.2.3

标签: python python-2.x


【解决方案1】:

简答

在 Python 2.x 中,str 类型保存原始字节,因此dis 假定如果您将字符串传递给它,它将获得编译字节码。它会尝试反汇编您作为字节码传递的字符串,并且——纯粹是由于 Python 字节码的实现细节——对于i in (2,3) 来说是成功的。但显然,它返回的是乱码。

在 Python 3.x 中,str 类型用于字符串bytes 类型用于原始字节,因此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. 我在 Python 2.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),测试成员资格,返回结果”),而您在 Python 2.7 中得到的是乱码。显然,dis 在 2.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)
    

    只是为了好玩,让我们通过在 Python 3 中将相同的字节传递给 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 版本而变化!)

【讨论】:

  • 这里是来自 Python2 文档 (docs.python.org/library/dis.html) 的文档的相应部分: >dis.dis([bytesource]) > 反汇编 bytesource 对象。 > bytesource 可以表示模块、类、方法、函数或代码对象,而在 Python 3 文档 (docs.python.org/dev/library/dis.html?highlight=dis#dis) 中它声明: >dis.dis(x=None) >Disassemble x 对象。 x 可以表示模块、类、方法、函数、代码对象、源代码字符串或原始字节码的字节序列。
【解决方案2】:

dis.dis 需要字节码作为参数,而不是 python 源代码。尽管您的第一个示例“有效”,但它没有提供任何有意义的输出。你可能想要:

import compiler, dis

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

这按预期工作。 (我只在 2.7 中测试过)。

【讨论】:

    【解决方案3】:

    如果您只是想获取简单表达式的字节码,将其作为 lambda 传递给 dis 并将您的表达式作为 lambda 的主体是最简单的:

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

    【讨论】:

      猜你喜欢
      • 2015-08-06
      • 2021-10-15
      • 2016-11-04
      • 2012-10-25
      • 2017-08-24
      • 2023-01-16
      • 1970-01-01
      • 2019-12-26
      相关资源
      最近更新 更多