In which scenario it is useful to use Disassembly on python?
The dis module can be effectively used to disassemble Python methods, functions and classes into low-level interpreter instructions.
I know that dis
information can be used for:
From your experience, do y开发者_StackOverflow中文版ou know any other scenarios where Disassembly Python feature could be useful?
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
onitem
(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
onitem
(opcode 31) - Call
append
onresult
(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 :)
I see the dis
module as being, essentially, a learning tool. Understanding what opcodes a certain snippet of Python code generates is a start to getting more "depth" to your grasp of Python -- rooting the "abstract" understanding of its semantics into a sample of (a bit more) concrete implementation. Sometimes the exact reason a certain Python snippet behaves the way it does may be hard to grasp "top-down" with pure reasoning from the "rules" of Python semantics: in such cases, reinforcing the study with some "bottom-up" verification (based on a possible implementation, of course -- other implementations would also be possible;-) can really help the study's effectiveness.
For day-to-day Python programming, not much. However, it is useful if you want to find out why doing something one way is faster than another way. I've also sometimes used it to figure out exactly how the interpreter handles some obscure bits of code. But really, I come up with a practical use-case for it very infrequently.
On the other hand, if your goal is to understand python rather than just being able to program in it, then it is an invaluable tool. For instance, ever wonder how function definition works? Here you go:
>>> def f():
... def foo(x=[1, 2, 3]):
... y = [4,]
... return x + y
...
>>> dis(f)
2 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
9 BUILD_LIST 3
12 LOAD_CONST 4 (<code object foo at 0xb7690770, file "<stdin>", line 2>)
15 MAKE_FUNCTION 1
18 STORE_FAST 0 (foo)
21 LOAD_CONST 0 (None)
24 RETURN_VALUE
You can see that this happens by pushing the constants 1, 2, and 3 onto the stack, putting what's in the stack into a list, loading that into a code object, making the code function into an object, and storing it into a variable foo.
精彩评论