Python状态机设计

jld*_*ont 45 python state-machine fsm

此Stack Overflow问题(C状态机设计)相关,Stack Overflow人员可以与我(以及社区)共享您的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
Run Code Online (Sandbox Code Playgroud)

但我确信在利用Python的动态特性(例如动态调度)的同时,还有很多方法可以实现.

我正在接受"引擎"的设计技术,它接收基于机器"状态"的"事件"和"发送".

S.L*_*ott 39

我真的不明白这个问题.该设计模式是相当清楚的.请参阅设计模式一书.

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()
Run Code Online (Sandbox Code Playgroud)

这是非常常见的样板,用于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" }
Run Code Online (Sandbox Code Playgroud)

在某些情况下,您的事件并不像测试对象的相等性那么简单,因此更通用的转换规则是使用正确的函数 - 对象对列表.

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") ]
Run Code Online (Sandbox Code Playgroud)

由于规则是按顺序评估的,因此允许使用"默认"规则.

  • 虽然你把它复制出书,看起来"更好"使用像`globals()[self.map [input]]()`(或像''S1':S1}`这样的词典)而不是eval?为了进一步挑剔,它正在重新定义内置的"下一个" (6认同)
  • @scott:我没有这本书.通过提出这个问题,我希望能够挖掘SO社区的集体智慧.您当然可以选择忽略该问题或做出贡献:这是您的选择. (3认同)
  • @jldupont:刚出书.没有发明它,只是从GoF设计模式书中复制它.这就是为什么我仍然没有得到你的问题 - 他们完全覆盖了这个问题. (2认同)
  • @scott:...,谢谢您的贡献。我不相信我有“抱怨”的意思,但是“抱怨”的定义当然属于“质的领域”。祝你有美好的一天! (2认同)

Pau*_*McG 12

在2009年4月的Python杂志中,我写了一篇关于使用pyparsing和imputil在Python中嵌入State DSL的文章.此代码允许您编写模块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)
Run Code Online (Sandbox Code Playgroud)

并且DSL编译器将创建所有必需的TrafficLight,红色,黄色和绿色类,以及适当的状态转换方法.代码可以使用以下内容调用这些类:

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()
Run Code Online (Sandbox Code Playgroud)

(不幸的是,imputil已经在Python 3中被删除了.)

  • Matt Anderson将此版本的statemachine代码提交给pyparsing wiki(http://pyparsing.wikispaces.com/message/view/Publications/18439845),该版本与Py2.7及更高版本兼容. (3认同)

Tre*_*ent 8

这种设计模式使用装饰实现状态机.从页面上的描述:

装饰器用于指定哪些方法是类的事件处理程序.

页面上也有示例代码(它很长,所以我不会在这里粘贴它).


Jon*_*han 5

我也对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
Run Code Online (Sandbox Code Playgroud)