[Tutor] PyMOTW: difflib

Kent Johnson kent37 at tds.net
Tue Apr 8 04:20:22 CEST 2008


Alan Gauld wrote:
> "Dick Moores" <rdm at rcblue.com> wrote 
> 
>> Could you give a blow-by-blow on the dis.dis()?
> 
> I'll have a go but I've never actually studied Python P-Code, 
> this is based on my 8080 and 68000 assembler experience!

Some hits, some misses. I'll correct to the best of my knowledge.

One thing you need to know is that the Python VM is a stack machine, not 
  register-based. Think Forth or Postscript, not 8080.
> 
>> In [22]: def f(x):
>>   ....:     if x%2:
>>   ....:         return "odd"
>>   ....:     else:
>>   ....:         return "even"
>>   ....:
>>   ....:
>>
>> In [23]: dis.dis(f)
>>  2           0 LOAD_FAST                0 (x)
>>              3 LOAD_CONST               1 (2)
> 
> load x and 2 into two temporary storage areas
> (registers in conventional assembler)

No, it pushes x and 2 onto the stack. 0 and 1 are the indices into the 
local variable list (in the stack frame) and the function's constant 
list (part of the function). The values in parentheses are helpful 
comments by dis, not part of the code itself.
> 
>>              6 BINARY_MODULO
> 
> Do the modulo operation and store the result in a temporary 
> location (accumulator in assembler)

The arguments are the top two items on the stack, the result is left on 
the stack.
> 
>>              7 JUMP_IF_FALSE            8 (to 18)
> 
> If the result is zero(false)

Yes, but not the way you think it works. It is checking the top of the 
stack (TOS in the docs), not some flag set by the modulo operation the 
way an 8080 would.

> go to instruction 18 
> (I don't know what the 8 signifies, I thought a
> program counter offset but that would make it 
> a jump to 15...)

Probably an offset to the PC value of the next instruction. Again, the 
part in parens is a helpful comment.
> 
>>             10 POP_TOP
> 
> else (ie modulo result is non zero) pop the top - I'm 
> not sure what that does exactly but I assume it pops 
> a value of some stack into the accumulator area?

Just pops the stack, the value is no longer needed after the test.
> 
>>  3          11 LOAD_CONST               2 ('odd')
>>             14 RETURN_VALUE
> 
> Load 'odd' into storage area 2 and then return it?

Load odd *from* offset 2 to the stack and return it.
> 
>>             15 JUMP_FORWARD             5 (to 23)
> 
> Not sure about this bit...

I think this is junk. The Python compiler does very little optimization, 
even simple peephole optimizations. This looks like boilerplate code 
emitted to skip the 'else' clause. The boilerplate is left in even 
though the code is not reachable.
> 
>>        >>   18 POP_TOP
> 
> This was target of our previous jump if false instruiction
> 
>>  5          19 LOAD_CONST               3 ('even')
>>             22 RETURN_VALUE
> 
> So it loads 'even' and then returns it to the caller.
> 
>>        >>   23 LOAD_CONST               0 (None)
>>             26 RETURN_VALUE
> 
> I assume this is the default return value in case there is no 
> other returns, so not used here.

More leftover junk.

BTW there are several recipes in the cookbook that rely on hacking the 
bytecodes directly from Python. Here are some:
http://tinyurl.com/6xn55p

This one by Raymond Hettinger optimizes references to globals by 
automatically converting them to references to locals. "The speed-up is 
substantial (trading one or more dictionary lookups for a single C-speed 
array lookup)."
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/277940

Kent


More information about the Tutor mailing list