之前做的项目中基本都是用到了有限状态机,直到python3.10才出现了match-case语句。之前直接很暴力的if-else实现了状态机的跳转。其实对于python来说,if-else的结构无可厚非,甚至在某些情况下比复杂的判断逻辑+跳转还要省内存。但是,这样子一是不够美观,二是属性全部对外暴露,并且当你想对状态机的总数进行修改或增加跳转时,往往会产生不可预料的问题。随着表的状态的扩展,状态之间的流转也会越来越复杂。
问:那既然3.10出来了,为啥不用match-case呢
因项目需求,python环境不一定都会是3.10版本。而且目前网上都流传match-case有性能上的问题,有时甚至不如if-else方便。我也不清楚为啥,但是总之先信了呗。毕竟match-case只能对单一对象进行判断(if真好用.jpg)。
作为专门为了python实现有限状态机的库,它肯定能够实现我们想到的功能。
conda等虚拟环境自己去找
pip install transitions
我们创建了两个对象:实体对象lump和状态机对象machine。
接下来操作时,设定状态机使用machine,运行状态机使用lump。
from transitions import Machine
class Matter(object):
pass
lump = Matter() # 创建实体对象lump
machine = Machine(model=lump) # 创建状态机对象machine
创建states的方法不止下面一种。但是这种是我觉得用着最舒服和最清晰的。
states列表中每一个列表元素代表了一个状态,name属性为状态的名字,on_enter属性表示进入状态时触发的函数(列表可为多个函数,顺序触发),on_exit表示退出状态时触发的函数(同上)。
同时因为pycharm的警告,后续pycharm会认为state变量未被初始化,所以我们直接在class内部初始化定义虚假的state,运行时该值会被再次修改。
参数 | 作用 | 是否必选 |
---|---|---|
name | 状态节点的名称 | √ |
on_enter | 进入状态节点时触发的事件 | X |
on_exit | 退出状态节点时触发的事件 | X |
需要注意的是,on_enter不会在初始化时被触发。
from transitions import Machine
class Matter(object):
def __init__(self): # 定义虚假的state,防止看着pycharm的警告不顺心
self.state = None
def init(self): print("Hello, init!")
def exit(self): print("Goodbye")
lump = Matter() # 创建实体对象lump
states = [
{'name': "00", 'on_enter': ['init']},
{'name': "01"},
{'name': "02"},
{'name': "09", 'on_enter': ['exit']},
{'name': "ERROR"},
] # 定义状态机的state
machine = Machine(model=lump, states=states, initial=states[0]['name']) # 创建状态机对象machine
还是一样,用自己觉得最舒服的添加方式。
参数 | 作用 | 是否必选 |
---|---|---|
trigger | transition的名称 | √ |
source | 原状态节点 | √ |
dest | 目标状态节点 | √ |
若transition名称重复,则只有第一条生效。
from transitions import Machine
class Matter(object):
def __init__(self): # 定义虚假的state,防止看着pycharm的警告不顺心
self.state = None
def init(self): print("Hello, init!")
def exit(self): print("Goodbye")
lump = Matter() # 创建实体对象lump
states = [
{'name': "00", 'on_enter': ['init']},
{'name': "01"},
{'name': "02"},
{'name': "09", 'on_enter': ['exit']},
{'name': "ERROR"},
] # 定义状态机的state
transitions = [
{'trigger': 'zero_to_one', 'source': '00', 'dest': '01'},
{'trigger': 'zero_to_two', 'source': '00', 'dest': '02'},
{'trigger': 'two_to_nine', 'source': '02', 'dest': '09'},
{'trigger': 'any_to_zero', 'source': '*', 'dest': '00'}, # 任意前状态 '*'
{'trigger': 'any_to_error', 'source': '*', 'dest': 'ERROR'},
] # 定义状态机的transitions
machine = Machine(model=lump, states=states, transitions=transitions, initial=states[0]['name']) # 创建状态机对象machine
不过多叙述了,自己试试就知道都有什么用了
from transitions import Machine
class Matter(object):
def __init__(self):
self.state = None
def init(self): print("Hello, init!")
def exit(self): print("Goodbye")
lump = Matter()
states = [
{'name': "00", 'on_enter': ['init']},
{'name': "01"},
{'name': "02"},
{'name': "09", 'on_exit': ['exit']},
{'name': "ERROR"},
]
transitions = [
{'trigger': 'zero_to_one', 'source': '00', 'dest': '01'},
{'trigger': 'zero_to_two', 'source': '00', 'dest': '02'},
{'trigger': 'two_to_nine', 'source': '02', 'dest': '09'},
{'trigger': 'any_to_zero', 'source': '*', 'dest': '00'}, # 任意前状态 '*'
{'trigger': 'any_to_error', 'source': '*', 'dest': 'ERROR'},
]
# machine.add_states(states=states)
# machine.add_transitions(transitions=transitions)
machine = Machine(model=lump, states=states, transitions=transitions, initial=states[0]['name'])
print(lump.state)
print(machine.get_triggers('00'))
lump.zero_to_two()
print(lump.state)
lump.two_to_nine()
print(lump.state)
lump.any_to_zero()
print(lump.state)