Why does Python's dis dislike lists?
python, python-2.x
Solution
Short Answer
In Python 2.x, the `str` type holds raw bytes, so `dis` assumes that if you pass it a string it is getting compiled bytecode. It tries to disassemble the string you pass it as bytecode and -- purely due to the implementation details of Python bytecode -- succeeds for `i in (2,3)`. Obviously, though, it returns gibberish.
In Python 3.x, the `str` type is for strings and the `bytes` types is for raw bytes, so `dis` can distinguish between compiled bytecode and strings -- and assumes it is getting source code if it gets a string.
Long Answer
Here's the thought process I followed to work this one out.
I tried it on my 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
Obviously, this works.
I tried it on 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
Aha! Notice also that the generated bytecode in Python 3.2 is what you would expect ("load `i`, load `(2,3)`, test for membership, return the result") whereas what you have got in Python 2.7 is gibberish. Clearly, `dis` is decompiling the string as bytecode in 2.7 but compiling it as Python in 3.2.
I had a look in the source code for `dis.dis`. Here are the key points:
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)
Just for fun, let's check this by passing the same bytes to `dis` in Python 3:
>>> 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>
Aha! Gibberish! (Though note that it's slightly different gibberish -- the bytecode has changed with the Python version!)
Problem
In Python (2.7.2),why does ``` import dis dis.dis("i in (2, 3)") ``` works as expected whereas ``` import dis dis.dis("i in [2, 3]") ``` raises: ``` 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 ``` Note that this doesn't affect Python3.