有没有更优雅的方式实现状态机行为的建议?

0 投票
3 回答
561 浏览
提问于 2025-04-17 12:33

为了简洁,我这里只展示了状态中可能发生的事情。我在状态机框架本身没有遇到什么奇怪的问题。

这里有一个具体的问题:

你觉得我们必须返回 StateChange(...)StateMachineComplete(...) 这两项,而其他一些动作,比如 some_action_1(...)some_action_2(...) 不需要返回——它们只是直接调用方法,这样是不是让你感到困惑?

我认为 StateChange(...) 需要返回,因为如果不返回,StateChange(...) 之后的代码会继续执行。这可不是状态机应该工作的方式!例如,看看下面的 ExampleState 中 event1 的实现。

import abc

class State(metaclass=abc.ABCMeta):

    # =====================================================================
    # == events the state optionally or must implement ====================
    # =====================================================================

    # optional: called when the state becomes active.
    def on_state_entry(self): pass

    # optional: called when we're about to transition away from this state.
    def on_state_exit(self): pass

    @abc.abstractmethod
    def event1(self,x,y,z): pass

    @abc.abstractmethod
    def event2(self,a,b): pass

    @abc.abstractmethod
    def event3(self): pass

    # =====================================================================
    # == actions the state may invoke =====================================
    # =====================================================================

    def some_action_1(self,c,d,e):
        # implementation omitted for brevity
        pass

    def some_action_2(self,f):
        # implementation omitted for brevity
        pass

class StateChange:

    def __init__(self,new_state_type):
        # implementation omitted for brevity
        pass

class StateMachineComplete: pass

class ExampleState(State):

    def on_state_entry(self):
        some_action_1("foo","bar","baz")

    def event1(self,x,y,z):

        if x == "asdf":
            return StateChange(ExampleState2)
        else:
            return StateChange(ExampleState3)

        print("I think it would be confusing if we ever got here.  Therefore the StateChange calls above are return")

    def event2(self,a,b):

        if a == "asdf":
            return StateMachineComplete()

        print("As with the event above, the return above makes it clear that we'll never get here.")

    def event3(self):

        # Notice that we're not leaving the state.  Therefore this can just be a method call, nothing need be returned.
        self.some_action_1("x","y","z")

        # In fact we might need to do a few things here.  Therefore a return call again doesn't make sense.
        self.some_action_2("z")

    # Notice we don't implement on_state_exit().  This state doesn't care about that.

3 个回答

0

我也曾经很难找到一个好的状态机解决方案在Python中。所以我写了一个叫做 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
1

把状态变化的代码写在程序里,会让整个过程变得复杂。状态机应该用简单的表格来驱动。一个表格的横轴是当前状态,另一个表格的纵轴是可能发生的事件。你需要准备两到三个表格:

  1. 一个“下一个状态”表,用来决定接下来要进入哪个状态。
  2. 一个“动作”表,用来决定要采取什么行动。
  3. 一个“读取”表,用来决定是继续处理当前的输入事件,还是转到下一个事件。

第三个表格是否需要,取决于输入的“语法”有多复杂。

还有一些更复杂的变种,但我觉得这三张表就够用了。

4

当我在Python中需要一个状态机的时候,我会把它存储为一个函数的字典。字典里的索引就是当前的状态,而这些函数会根据需要执行相应的操作,并返回下一个状态(这个状态可能和当前状态是一样的)以及输出结果。操作这个状态机其实很简单:

state, outputs = machine_states[state](inputs)

撰写回答