Python code written in 1998, how to improve/change it?

Carl Cerecke cdc at maxnet.co.nz
Thu Jan 19 16:27:58 EST 2006


Petr Jakes wrote:
> Hello,
> I am trying to study/understand OOP principles using Python. I have
> found following code http://tinyurl.com/a4zkn about FSM (finite state
> machine) on this list, which looks quite useful for my purposes. As
> this code was posted long time ago (November 1998) I would like to ask
> if the principles used in this code are still valid in the "modern"
> Python and if/how it can be improved (revrited) using futures of
> current version of Python.
> Thanks for your comments for a "newbie" and for your patience.
> Petr Jakes
> 

Python has no goto.

If you think about the possible execution paths through a function, you 
can represent that as a finite state machine: A FSM is just a graph, and 
in a function, you get a FSM by treating each statement as a node, and 
statements that can follow that statement (in the execution order) have 
a (directed) edge to them (let's ignore exceptions for the moment). So for:

a=b
if a == 3:
  z = 0
else:
  z = 1
print 'spam'

we get:
(a=b) -> (if a == 3) -> (z = 0)
                |            |
             (z = 1) -> (print 'spam)

Now, when we want to emulate a FSM in a function directly (rather than 
some slower table-driven scheme), we need to use the constructs provided 
by the language. But simple 'if' statements just don't cut it. We end up 
with:

while state != 'final':
   if state == 1:
     # do state 1 stuff here
     state = 3
   elif state == 2:
     # if cond:
       state == 1
     else:
       state == 99
   elif
     ...
   else:
     # unknown state

Problem with this approach is that, on average, you'll have n/2 
comparisons of the state variable. What you want is to jump directly 
from state 1 to state 3 without having to go anywhere else. You want a goto.

Another goto-less way is with functions. Each function is a state, which 
sets a global function variable to the next state-function:

def f_state_1():
   global func
   # do state 1
   func = f_state_3

def f_state_2():
   global func
   # do state_2 stuff
   if cond:
     func = f_state_1
   else:
     func = f_state_99

#etc.

func = f_state_1 # start_state

while func != None:
   func()


We've eliminated the numerous comparisons, and it is, arguably, more 
pythonic. But now you have a function-call overhead on each state 
transition, and any information shared between states has to be in an 
enclosing scope.

We want a goto.

Unfortunately, this is about the only problem I can think of where gotos 
are useful. And for reasons well explained elsewhere, gotos are Not Good.

I've solved this in a very efficient, but rather unpythonic way.

First, you write (or generate) the code in the first way indicated 
above. Lots of inefficient 'if' statements in one big function (say, 
'fsm'). Then, you write another function (say 'gotoize') which takes fsm 
as an argument, and *changes the byte code* of the fsm code object to 
add JUMP_ABSOLUTE bytecodes (i.e. gotos) where the 'state' variable gets 
reassigned.
fsm can then be decorated with gotoize, and you have a very fast (for 
python) FSM. You are, essentially, doing some (fairly simple) byte-code 
optimisation.

I can dig out the code if you are interested (except it's pretty untidy 
at the moment. I'd be ashamed to show it in it's current state :-)

Ooops. I just reread your post and you claim (or is that plead?) 
"newbie" status. Sorry if this is over your head. Hope it helps anyway.

Cheers,
Carl.



More information about the Python-list mailing list