Help with coroutine-based state machines?

Terry Reedy tjreedy at udel.edu
Thu May 29 14:59:53 EDT 2003


"Alan Kennedy" <alanmk at hotmail.com> wrote in message
news:3ED622CA.4B01E3C1 at hotmail.com...
> Turning generator-functions into generator-iterators.
> -----------------------------------------------------
>
> In order to do away with the jump table, I decided that all names
> (in the instance dictionary) which referred to generator-functions
> be changed so that they referred instead to the corresponding
> instantiated generator-iterators. This would mean that state changes
> would be indicated by a statement like
>
> yield self.newstate

I very much like the idea of factoring out the unneeded redundancy of
representing states by name strings instead of by the state objects
themselves.

> To do this, here is the little bit of hackery that I came up with:
> at __init__ time of the state-machine object, it modifies the
> instance dictionary, replacing generator-functions with their
> instantiated generator-iterators.

I found this somewhat misleading when trying to understand the code.
The generator functions, as instance methods, are class attributes in
the *class* dictionary.  They should typically be called exactly once
for each instance of the machine, with that instance as the argument.
The obvious place to do this is in .__init__().  So I would not call
this hackery.

The resulting gen-it objects, being instance specific, belong and get
put in the *instance* dict.  Again, this is not hackery.  Because you
used the same names, they overshadow rather than replace their
namesakes, which continue to sit, unchanged,  in the class dict.  So
> Furthermore, it won't be possible to create a new
> generator-iterator, because we've overwritten the reference to the
> generator-function in the instance dictionary!
is incorrect.  You can still access FSM.idle as exactly that, even
from the same instance.

This shadowing could be called hackery, but it is unnecessary.  Give
either the gen-funcs or the gen-its an affix to differentiate the two
types.  For instance, give the former a 'g_' prefix,  as in "    def
g_idle(self): ...", and add the prefix to the calls, so that
>      self.__dict__[name] = eval ('self.%s()' % name)
becomes
        self.__dict__[name] = eval ('self.g_%s()' % name)

There is also a little more redundancy to squeeze out.  You only need
the gen-its to access their .next methods and the lookup only needs to
be done once, in .__init__, rather than with each state switch.  So
you could move '.next' from .dispatch to .__init__ to get

    self.__dict__[name] = eval ('self.g_%s()' % name).next
and
    next = newstate()
(instead of newstate.next()).  In other words, represent states by the
corresponding .next methods instead of the gen-its that have those
methods.  Since the .next functions do not have parameters, not even
self (since self is bound within by the gen-func call), they also
belong in the instance dict.

Terry J. Reedy






More information about the Python-list mailing list