说明
首先FSM是一个很有用的工具,在程序设计中,某个对象会对应若干不同的状态,在这个状态下,同样的方法会有不一样的行为。
python有个transitions包可以做这个,过去一直不想用,主要是感觉有点鸡肋。
本质上,FSM是一个较小的图,很自然地应该属于图的内容。在图这块,我使用networkx和neo4j来进行存储和处理。所以,理论上是可以自己实现transitions的功能的。
但是(肯定有但是),理论上可行与实际上的实现还有差距,我不太确定哪里没想明白,所以就先无脑的先使用这个包,来实现一些常见的需求。
内容
1 包信息
这个项目的github项目在这里
安装过程比较简单
pip install transitions
核心的功能(价值)我认为是这么一张图(虽然画图的环境也不是很好装)
2 使用
安装上还是使用豆瓣的源!pip install transitions -i https://mirrors.aliyun.com/pypi/simple/
关于原理就不多深究了,以下就看几个简单的使用,然后和应用场景关联起来。
2.1 基本概念
引入Machine对象,然后对于任意一个新的自建对象进行封装,然后就可以使用了
from transitions import Machine
class Switch:
pass
# 定义状态机的状态
states = ['off', 'on']
# 定义状态机的转换规则
transitions = [
{'trigger': 'switch_on', 'source': 'off', 'dest': 'on'},
{'trigger': 'switch_off', 'source': 'on', 'dest': 'off'},
{'trigger': 'switch_on', 'source': 'on', 'dest': 'on'}
]
# 创建状态机
machine = Machine(model=Switch, states=states, transitions=transitions, initial='off')
# 使用示例
switch = Switch()
# 初始状态为关闭
print(switch.state)
off
# 打开开关
switch.switch_on()
print(switch.state)
on
# 再次打开开关,但已经是开启状态,不产生状态变化
switch.switch_on()
print(switch.state)
on
状态(states)就是图里的点,transitions就是若干条边,其中第一个参数是边的类型(switch_on,switch_off),Machine对象会把这个变为实例的方法。
Action: 一个点和一条边的类型决定了一个动作。例如从 on 这个节点出发,有switch_on,switch_off两条边可以走,选择任意一条边,必然会达到一个终点on(环回)或者off。
基于这个基本概念,看下面两个更接近实战的例子
2.2 根据状态进行不同处理
这个可能是我目前最需要的应用,定义好不同的状态,每个状态都要进行处理,只不过处理的内容和方法可以根据状态而变。
from transitions import Machine
class ComplexMachine:
pass
def processing(self):
if self.state =='active':
print('I am active')
elif self.state =='broken':
print('Broken, needs repair')
else:
print('else')
# 定义状态机的状态
states = ['idle', 'active', 'broken']
# 定义状态机的转换规则
transitions = [
{'trigger': 'activate', 'source': 'idle', 'dest': 'active'},
{'trigger': 'deactivate', 'source': 'active', 'dest': 'idle'},
{'trigger': 'break_machine', 'source': ['idle', 'active'], 'dest': 'broken'},
{'trigger': 'repair_machine', 'source': 'broken', 'dest': 'idle'}
]
idle, active 和 broken可以对应于我的BT2对象的init, running, stop,所以其实还挺容易匹配上的。
就本例而言,这对应于一个机器的一般状态。把这个当成一个容器的控制对象也挺合适的。
# 创建状态机
machine = Machine(model=ComplexMachine, states=states, transitions=transitions, initial='idle')
# 使用示例
complex_machine = ComplexMachine()
# 初始状态为idle
print(complex_machine.state) # 输出: idle
# 激活机器
complex_machine.activate()
print(complex_machine.state) # 输出: active
complex_machine.processing()
I am active
# 破坏机器
complex_machine.break_machine()
print(complex_machine.state) # 输出: broken
complex_machine.processing()
Broken, needs repair
# 修复机器
complex_machine.repair_machine()
print(complex_machine.state) # 输出: idle
# 试图在broken状态下激活机器,但状态不会发生变化
complex_machine.break_machine()
complex_machine.activate()
print(complex_machine.state) # 输出: broken
MachineError: "Can't trigger event activate from state broken!"
2.3 在执行之前的处理
这部分有点小小的问题,但还是能用。这有点像flask的before_handler, 在请求之前执行的动作。问题是函数的传参比较奇怪,必须把实例作为参数传进去。
from transitions import Machine
class ComplexMachine:
def __init__(self):
self.activation_message = None
def set_activation_message(self, *args, **kwargs):
self.activation_message = kwargs.get('message', None)
def print_activation_message(self):
print(f"Activation Message: {self.activation_message}")
# 定义状态机的状态
states = ['idle', 'active', 'broken']
# 定义状态机的转换规则
transitions = [
{'trigger': 'activate', 'source': 'idle', 'dest': 'active', 'before': 'set_activation_message'},
{'trigger': 'deactivate', 'source': 'active', 'dest': 'idle'},
{'trigger': 'break_machine', 'source': ['idle', 'active'], 'dest': 'broken'},
{'trigger': 'repair_machine', 'source': 'broken', 'dest': 'idle'}
]
实例化
# 导入 transitions 模块
from transitions import Machine
# 创建状态机
machine = Machine(model=ComplexMachine, states=states, transitions=transitions, initial='idle')
# 创建 ComplexMachine 实例
complex_machine = ComplexMachine()
# 输出初始状态
print(f"Current State: {complex_machine.state}")
# 触发激活操作
complex_machine.activate(complex_machine,message="Machine is now active")
Current State: idle
True
# 输出激活后的状态和消息
print(f"Current State: {complex_machine.state}")
complex_machine.print_activation_message()
Current State: active
Activation Message: Machine is now active
3 总结
以上,已经可以使用transitions 用于一类简单的应用:进行非常小的图状态管理。从图的查询来说,是从某个点触发,某一个有向边指向的下一个节点。