dis
is useful, for example, when you have different code doing the same thing and you wonder where the performance difference lies in.
Example: list += [item]
vs list.append(item)
def f(x): return 2*x
def f1(func, nums):
result = []
for item in nums:
result += [fun(item)]
return result
def f2(func, nums):
result = []
for item in nums:
result.append(fun(item))
return result
timeit.timeit
says that f2(f, range(100))
is approximately twice as fast than f1(f, range(100)
. Why?
(Interestingly f2
is roughly as fast as map(f, range(100))
is.)
f1
You can see the whole output of dis by calling dis.dis(f1)
, here is line 4.
4 19 LOAD_FAST 2 (result)
22 LOAD_FAST 1 (fun)
25 LOAD_FAST 3 (item)
28 CALL_FUNCTION 1
31 BUILD_LIST 1
34 INPLACE_ADD
35 STORE_FAST 2 (result)
38 JUMP_ABSOLUTE 13
>> 41 POP_BLOCK
f2
Again, here is only line 4:
4 19 LOAD_FAST 2 (result)
22 LOAD_ATTR 0 (append)
25 LOAD_FAST 1 (fun)
28 LOAD_FAST 3 (item)
31 CALL_FUNCTION 1
34 CALL_FUNCTION 1
37 POP_TOP
38 JUMP_ABSOLUTE 13
>> 41 POP_BLOCK
Spot the difference
In f1
we need to:
- Call
fun
on item
(opcode 28)
- Make a list out of it (opcode 31, expensive!)
- Add it to
result
(opcode 34)
- Store the returned value in
result
(opcode 35)
In f2
, instead, we just:
- Call
fun
on item
(opcode 31)
- Call
append
on result
(opcode 34; C code: fast!)
This explains why the (imho) more expressive list += [value]
is much slower than the list.append()
method.
Other than that, dis.dis
is mainly useful for curiosity and for trying to reconstruct code out of .pyc
files you don't have the source of without spending a fortune :)