Python 状态机设计

52 投票
12 回答
56788 浏览
提问于 2025-04-15 18:15

关于这个Stack Overflow的问题(C状态机设计),请问大家能分享一下你们在Python中设计状态机的技巧吗?我相信这对我和大家都会有帮助。

目前,我的想法是基于以下内容来构建一个引擎:

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的动态特性来实现,比如动态调度。

我想要的是一种设计技巧,用于“引擎”,它能够接收“事件”,并根据机器的“状态”进行相应的“调度”。

12 个回答

9

这里有一个关于使用装饰器来实现状态机的设计模式,具体可以参考这个链接。页面上是这样描述的:

装饰器用于指定哪些方法是这个类的事件处理器。

页面上还有示例代码,不过因为代码比较长,我就不在这里贴出来了。

12

在2009年4月的《Python Magazine》杂志上,我写了一篇关于如何在Python中嵌入状态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编译器会自动创建所有需要的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()

(不幸的是,imputil在Python 3中已经被去掉了。)

40

我不太明白这个问题。状态设计模式其实挺简单的。可以看看这本设计模式的书

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把类名转换成实际的类。另一种方法是把转换规则做成实例变量,而不是类变量,然后在所有类定义完后再创建实例。

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" }

在某些情况下,你的事件可能不只是简单地测试对象是否相等,这时候可以用一个合适的函数-对象对的列表来作为更通用的转换规则。

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") ]

因为这些规则是按顺序评估的,所以可以设置一个“默认”规则。

撰写回答