Python状态机devise
与这个SO问题(C状态机devise)相关 ,你能帮我分享一下你的Python状态机devise技术吗?
更新3 :目前,我正在寻找一个基于以下的引擎:
class TrackInfoHandler(object): def __init__(self): self._state="begin" self._acc="" ## ================================== Event callbacks def startElement(self, name, attrs): self._dispatch(("startElement", name, attrs)) def characters(self, ch): self._acc+=ch def endElement(self, name): self._dispatch(("endElement", self._acc)) self._acc="" ## =================================== def _missingState(self, _event): raise HandlerException("missing state(%s)" % self._state) def _dispatch(self, event): methodName="st_"+self._state getattr(self, methodName, self._missingState)(event) ## =================================== State related callbacks
但是我相信在利用Python的dynamic特性(如dynamic调度)的同时,还有很多方法可以实现。
Update2 :我是在“引擎”的devise技术之后接收“事件”和“调度”的机器的“状态”的基础上。
我真的不明白这个问题。 国家devise模式非常清晰。 请参阅devise模式书 。
class SuperState( object ): def someStatefulMethod( self ): raise NotImplementedError() def transitionRule( self, input ): raise NotImplementedError() class SomeState( SuperState ): def someStatefulMethod( self ): actually do something() def transitionRule( self, input ): return NextState()
这是相当普遍的样板,在Java,C ++,Python中使用(我也相信其他语言)。
如果你的状态转换规则碰巧是微不足道的,有一些优化将转换规则本身推入超类。
请注意,我们需要有前向引用,所以我们通过名称引用类,并使用eval
将类名转换为实际的类。 另一种方法是创build转换规则实例variables而不是类variables,然后在定义所有类之后创build实例。
class State( object ): def transitionRule( self, input ): return eval(self.map[input])() class S1( State ): map = { "input": "S2", "other": "S3" } pass # Overrides to state-specific methods class S2( State ): map = { "foo": "S1", "bar": "S2" } class S3( State ): map = { "quux": "S1" }
在某些情况下,事件并不像testing对象是否相等那么简单,所以更一般的过渡规则是使用适当的函数对象列表。
class State( object ): def transitionRule( self, input ): next_states = [ s for f,s in self.map if f(input) ] assert len(next_states) >= 1, "faulty transition rule" return eval(next_states[0])() class S1( State ): map = [ (lambda x: x == "input", "S2"), (lambda x: x == "other", "S3" ) ] class S2( State ): map = [ (lambda x: "bar" <= x <= "foo", "S3"), (lambda x: True, "S1") ]
由于规则是按顺序评估的,所以这允许一个“默认”规则。
在2009年4月的“Python杂志”上,我写了一篇关于在Python中embedded状态DSL的文章,使用pyparsing和imputil。 这段代码将允许你写模块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)
DSL编译器将创build所有必要的TrafficLight,Red,Yellow和Green类以及适当的状态转换方法。 代码可以使用像这样的东西来调用这些类:
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()
(不幸的是,Python 3中已经放弃了imputil)
有使用装饰器来实现状态机的这种devise模式 。 从页面上的描述:
装饰器用于指定哪些方法是类的事件处理程序。
在页面上也有示例代码(这是很长的,所以我不会在这里粘贴)。
我也不满意state_machines的当前选项,所以我写了state_machine库
你可以通过pip install state_machine
来安装它,并像这样使用它:
@acts_as_state_machine class Person(): name = 'Billy' sleeping = State(initial=True) running = State() cleaning = State() run = Event(from_states=sleeping, to_state=running) cleanup = Event(from_states=running, to_state=cleaning) sleep = Event(from_states=(running, cleaning), to_state=sleeping) @before('sleep') def do_one_thing(self): print "{} is sleepy".format(self.name) @before('sleep') def do_another_thing(self): print "{} is REALLY sleepy".format(self.name) @after('sleep') def snore(self): print "Zzzzzzzzzzzz" @after('sleep') def big_snore(self): print "Zzzzzzzzzzzzzzzzzzzzzz" person = Person() print person.current_state == person.sleeping # True print person.is_sleeping # True print person.is_running # False person.run() print person.is_running # True person.sleep() # Billy is sleepy # Billy is REALLY sleepy # Zzzzzzzzzzzz # Zzzzzzzzzzzzzzzzzzzzzz print person.is_sleeping # True
我认为S.洛特的答案是一个更好的方法来实现一个状态机,但是如果你仍然想继续你的方法,使用(state,event)
作为你的dict
的关键是更好的。 修改你的代码:
class HandlerFsm(object): _fsm = { ("state_a","event"): "next_state", #... }
这可能取决于你的状态机是多么复杂。 对于简单的状态机来说,dicts字典(用于DFA的状态键的事件键,或者用于NFA的状态键的列表/集合/元组的事件键)可能是最简单的事情。
对于更复杂的状态机,我听说过SMC的好处, SMC可以编译声明式状态机描述,以各种语言(包括Python)进行编码。
我认为PySCXML工具也需要仔细观察。 该项目使用W3C定义: 状态图表XML(SCXML) :用于控制抽象的状态机表示法
SCXML提供了一个基于CCXML和Harel状态表的基于状态机的通用执行环境
目前,SCXML是一个工作草案, 但很快就会得到W3C的build议(这是第9份草案)。
另一个需要强调的要点是有一个Apache Commons项目,旨在创build和维护一个Java SCXML引擎,能够执行一个使用SCXML文档定义的状态机,同时抽象出环境接口……而对于某些支持这种工具的工具技术将在未来出现,当SCXML离开它的草稿状态…
我绝对不会推荐自己实现这样一个众所周知的模式。 如果您需要自定义function,只需转换为开放源代码实现,然后包装另一个类。 在这篇文章中,我解释了为什么我更喜欢这个特定的实现及其特性。
我不认为达到处理XML的有限状态机。 通常的做法,我认为是使用堆栈:
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)
对于每种元素,只需要一个支持addCharacters
, addElement
和close
方法的类。
编辑:澄清,是的,我的意思是争辩说,有限状态机通常是错误的答案,作为一个通用的编程技术,他们是垃圾,你应该远离。
有一些真正理解清楚的问题,FSMs是一个很好的解决scheme。 例如, lex
是好东西。
也就是说,FSM通常不能很好地应对变化。 假设有一天你想添加一些状态,也许是“我们看过元素X了吗? 旗。 在上面的代码中,你添加一个布尔属性到适当的元素类,你就完成了。 在一个有限状态机中,你将状态和转换次数加倍。
首先需要有限状态的问题经常会演变为需要更多的状态,或许是一个数字 ,在这一点上,或者你的FSMscheme是烤面包,或者更糟的是,你演变成某种广义的状态机,真的有麻烦了。 你走得越远,你的规则开始像代码一样行动起来 – 但是你用别人不知道的发明的缓慢解释语言编写代码,没有debugging器,也没有工具。
下面的代码是一个非常简单的解决scheme。 唯一有趣的部分是:
def next_state(self,cls): self.__class__ = cls
每个状态的所有逻辑都包含在一个单独的类中。 通过replace正在运行的实例的' __class__ '来改变'状态'。
#!/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
结果:
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}
检查这个和这个 。
一个相关的项目是marmoolak 。 这是一个关于它的描述 。