Python实现状态机

对于一个状态机,最基本的要素就是状态和事件,所以根据这个思路,我们可以设计一个具备基本功能的状态机。

以看碟片为例,DVD的状态包含:已开机,正在播放,正在暂停,已关机。而触发这些状态的事件有:遥控开机,遥控播放,遥控暂停,遥控关机。所以画一个状态转换表如下:
在这里插入图片描述
首先,设计状态基类。

class FsmState:
    def enter(self, event, fsm):  ## 参数event为触发状态转换的事件, fsm则为状态所在状态机
        pass
    
    def exit(self, fsm):
        pass

FsmState将是我们这个状态机所有状态的基类。enter函数用于执行进入该状态的操作,exit函数用于执行离开该状态的操作。

然后我们将例子中的四个状态转换为代码:

class DvdPowerOn(FsmState):
    def enter(self, event, fsm):
        print("dvd is power on")

class DvdPlaying(FsmState):
    def enter(self, event, fsm):
        print("dvd is going to play")

    def exit(self, fsm):
        print("dvd stoped play")

class DvdPausing(FsmState):
    def enter(self, event, fsm):
        print("dvd is going to pause")

    def exit(self, fsm):
        print("dvd stopped pause")

class DvdPowerOff(FsmState):
    def enter(self, event, fsm):
        print("dvd is power off")

接下来,设计事件基类。

class FsmEvent:
    pass

FsmEvent将是我们这个状态机中所有事件的基类。这个事件中的所有成员,均由子类自由添加。
子事件如下:

class PowerOnEvent(FsmEvent):
    pass

class PowerOffEvent(FsmEvent):
    pass

class PlayEvent(FsmEvent):
    pass

class PauseEvent(FsmEvent):
    pass

然后是我们的状态机基类,用于将我们的状态和事件联系起来。

from collections import namedtuple

Transaction = namedtuple("Transaction", ["prev_state", "event", "next_state"])    ## 一次状态转换的所有要素:上一个状态--事件-->下一个状态

class FSM:
    def __init__(self, context):                                                  ## context:状态机上下文
        self.context = context
        self.state_transaction_table = []                                         ## 常规状态转换表
        self.global_transaction_table = []                                        ## 全局状态转换表
        self.current_state = None
        self.working_state = FsmState

    def add_global_transaction(self, event, end_state):     # 全局转换,直接进入到结束状态
        if not issubclass(end_state, FsmFinalState):
            raise FsmException("The state should be FsmFinalState")
        self.global_transaction_table.append(Transaction(self.working_state, event, end_state))

    def add_transaction(self, prev_state, event, next_state):
        if issubclass(prev_state, FsmFinalState):
            raise FsmException("It's not allowed to add transaction after Final State Node")
        self.state_transaction_table.append(Transaction(prev_state, event, next_state))

    def process_event(self, event):
        for transaction in self.global_transaction_table:
            if isinstance(event, transaction.event):
                self.current_state = transaction.next_state()
                self.current_state.enter(event, self)
                self.clear_transaction_table()
                return
        for transaction in self.state_transaction_table:
            if isinstance(self.current_state, transaction.prev_state) and isinstance(event, transaction.event):
                self.current_state.exit(self.context)
                self.current_state = transaction.next_state()
                self.current_state.enter(event, self)
                if isinstance(self.current_state, FsmFinalState):
                    self.clear_transaction_table()
                return
        raise FsmException("Transaction not found")
    
    def clear_transaction_table(self):
        self.global_transaction_table = []
        self.state_transaction_table = []
        self.current_state = None

    def run(self):
        if len(self.state_transaction_table) == 0: return
        self.current_state = self.state_transaction_table[0].prev_state()
        self.current_state.enter(None, self)

    def isRunning(self):
        return self.current_state is not None

    def next_state(self, event):
        for transaction in self.global_transaction_table:
            if isinstance(event, transaction.event):
                return transaction.next_state
        for transaction in self.state_transaction_table:
            if isinstance(self.current_state, transaction.prev_state) and isinstance(event, transaction.event):
                return transaction.next_state
        return None

在上述代码中看到的FsmException定义如下:

class FsmException(Exception):
    def __init__(self, description):
        super().__init__(description)

所以最后,设计我们的DVD类。

class DVD(FSM):
    def __init__(self):
        super().__init__(None)

让DVD类运行起来:

dvd = DVD();
dvd.add_transaction(DvdPowerOff, PowerOnEvent, DvdPowerOn)
dvd.add_transaction(DvdPowerOn, PowerOffEvent, DvdPowerOff)
dvd.add_transaction(DvdPowerOn, PlayEvent, DvdPlaying)
dvd.add_transaction(DvdPlaying, PowerOffEvent, DvdPowerOff)
dvd.add_transaction(DvdPlaying, PauseEvent, DvdPausing)
dvd.add_transaction(DvdPausing, PowerOffEvent, DvdPowerOff)
dvd.add_transaction(DvdPausing, PlayEvent, DvdPlaying)
dvd.run()

dvd.process_event(PowerOnEvent())
dvd.process_event(PlayEvent())
dvd.process_event(PauseEvent())
dvd.process_event(PlayEvent())
dvd.process_event(PowerOffEvent())

运行结果如下:

dvd is power off
dvd is power on
dvd is going to play
dvd stoped play
dvd is going to pause
dvd stopped pause
dvd is going to play
dvd stoped play
dvd is power off
  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值