Python state-machine design

后端 未结 12 2106
予麋鹿
予麋鹿 2020-11-30 17:36

Related to this Stack Overflow question (C state-machine design), could you Stack Overflow folks share your Python state-machine design techniques

相关标签:
12条回答
  • 2020-11-30 18:22

    The following code is a really simple solution. The only interesting part is:

       def next_state(self,cls):
          self.__class__ = cls
    

    All the logic for each state is contained in a separate class. The 'state' is changed by replacing the '__class__' of the running instance.

    #!/usr/bin/env python
    
    class State(object):
       call = 0 # shared state variable
       def next_state(self,cls):
          print '-> %s' % (cls.__name__,),
          self.__class__ = cls
    
       def show_state(self,i):
          print '%2d:%2d:%s' % (self.call,i,self.__class__.__name__),
    
    class State1(State):
       __call = 0  # state variable
       def __call__(self,ok):
          self.show_state(self.__call)
          self.call += 1
          self.__call += 1
          # transition
          if ok: self.next_state(State2)
          print '' # force new line
    
    class State2(State):
       __call = 0
       def __call__(self,ok):
          self.show_state(self.__call)
          self.call += 1
          self.__call += 1
          # transition
          if ok: self.next_state(State3)
          else: self.next_state(State1)
          print '' # force new line
    
    class State3(State):
       __call = 0
       def __call__(self,ok):
          self.show_state(self.__call)
          self.call += 1
          self.__call += 1
          # transition
          if not ok: self.next_state(State2)
          print '' # force new line
    
    if __name__ == '__main__':
       sm = State1()
       for v in [1,1,1,0,0,0,1,1,0,1,1,0,0,1,0,0,1,0,0]:
          sm(v)
       print '---------'
       print vars(sm
    

    Result:

     0: 0:State1 -> State2 
     1: 0:State2 -> State3 
     2: 0:State3 
     3: 1:State3 -> State2 
     4: 1:State2 -> State1 
     5: 1:State1 
     6: 2:State1 -> State2 
     7: 2:State2 -> State3 
     8: 2:State3 -> State2 
     9: 3:State2 -> State3 
    10: 3:State3 
    11: 4:State3 -> State2 
    12: 4:State2 -> State1 
    13: 3:State1 -> State2 
    14: 5:State2 -> State1 
    15: 4:State1 
    16: 5:State1 -> State2 
    17: 6:State2 -> State1 
    18: 6:State1 
    ---------
    {'_State1__call': 7, 'call': 19, '_State3__call': 5, '_State2__call': 7}
    
    0 讨论(0)
  • 2020-11-30 18:22

    Other related projects:

    • http://fsme.sourceforge.net/

    • https://code.google.com/p/visio2python/

    You can paint state-machine and then use it in your code.

    0 讨论(0)
  • 2020-11-30 18:23

    I wouldn't think to reach for a finite state machine for handling XML. The usual way to do this, I think, is to use a stack:

    class TrackInfoHandler(object):
        def __init__(self):
            self._stack=[]
    
        ## ================================== Event callbacks
    
        def startElement(self, name, attrs):
            cls = self.elementClasses[name]
            self._stack.append(cls(**attrs))
    
        def characters(self, ch):
            self._stack[-1].addCharacters(ch)
    
        def endElement(self, name):
            e = self._stack.pop()
            e.close()
            if self._stack:
                self._stack[-1].addElement(e)
    

    For each kind of element, you just need a class that supports the addCharacters, addElement, and close methods.

    EDIT: To clarify, yes I do mean to argue that finite state machines are usually the wrong answer, that as a general-purpose programming technique they're rubbish and you should stay away.

    There are a few really well-understood, cleanly-delineated problems for which FSMs are a nice solution. lex, for example, is good stuff.

    That said, FSMs typically don't cope well with change. Suppose someday you want to add a bit of state, perhaps a "have we seen element X yet?" flag. In the code above, you add a boolean attribute to the appropriate element class and you're done. In a finite state machine, you double the number of states and transitions.

    Problems that require finite state at first very often evolve to require even more state, like maybe a number, at which point either your FSM scheme is toast, or worse, you evolve it into some kind of generalized state machine, and at that point you're really in trouble. The further you go, the more your rules start to act like code—but code in a slow interpreted language you invented that nobody else knows, for which there's no debugger and no tools.

    0 讨论(0)
  • 2020-11-30 18:27

    There is this design pattern for using decorators to implement state machines. From the description on the page:

    Decorators are used to specify which methods are the event handlers for the class.

    There is example code on the page as well (it is quite long so I won't paste it here).

    0 讨论(0)
  • 2020-11-30 18:27

    I would definitely not recommend implementing such a well known pattern yourself. Just go for an open source implementation like transitions and wrap another class around it if you need custom features. In this post I explain why I prefer this particular implementation and its features.

    0 讨论(0)
  • 2020-11-30 18:30

    In the April, 2009 issue of Python Magazine, I wrote an article on embedding a State DSL within Python, using pyparsing and imputil. This code would allow you to write the module trafficLight.pystate:

    # trafficLight.pystate
    
    # define state machine
    statemachine TrafficLight:
        Red -> Green
        Green -> Yellow
        Yellow -> Red
    
    # define some class level constants
    Red.carsCanGo = False
    Yellow.carsCanGo = True
    Green.carsCanGo = True
    
    Red.delay = wait(20)
    Yellow.delay = wait(3)
    Green.delay = wait(15)
    

    and the DSL compiler would create all the necessary TrafficLight, Red, Yellow, and Green classes, and the proper state transition methods. Code could call these classes using something like this:

    import statemachine
    import trafficLight
    
    tl = trafficLight.Red()
    for i in range(6):
        print tl, "GO" if tl.carsCanGo else "STOP"
        tl.delay()
        tl = tl.next_state()
    

    (Unfortunately, imputil has been dropped in Python 3.)

    0 讨论(0)
提交回复
热议问题