有没有更优雅的方式实现状态机行为的建议?
为了简洁,我这里只展示了状态中可能发生的事情。我在状态机框架本身没有遇到什么奇怪的问题。
这里有一个具体的问题:
你觉得我们必须返回 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
把状态变化的代码写在程序里,会让整个过程变得复杂。状态机应该用简单的表格来驱动。一个表格的横轴是当前状态,另一个表格的纵轴是可能发生的事件。你需要准备两到三个表格:
- 一个“下一个状态”表,用来决定接下来要进入哪个状态。
- 一个“动作”表,用来决定要采取什么行动。
- 一个“读取”表,用来决定是继续处理当前的输入事件,还是转到下一个事件。
第三个表格是否需要,取决于输入的“语法”有多复杂。
还有一些更复杂的变种,但我觉得这三张表就够用了。
4
当我在Python中需要一个状态机的时候,我会把它存储为一个函数的字典。字典里的索引就是当前的状态,而这些函数会根据需要执行相应的操作,并返回下一个状态(这个状态可能和当前状态是一样的)以及输出结果。操作这个状态机其实很简单:
state, outputs = machine_states[state](inputs)