如何为“转换”状态机定义触发器枚举?
How to define enum of Trigger for `transitions` state machine?
作为 的无关后续,它使用以下工作代码:
from transitions import Machine
from transitions import EventData
from typing import Callable
from enum import Enum, auto
class Observer:
def state_changed(self, event_data: EventData):
print(f"state is now '{event_data.state.name}'")
class State(Enum):
SOLID = auto()
LIQUID = auto()
GAS = auto()
class SubscribableMachine(Machine):
transitions = [
{'trigger': 'heat', 'source': State.SOLID, 'dest': State.LIQUID},
{'trigger': 'heat', 'source': State.LIQUID, 'dest': State.GAS},
{'trigger': 'cool', 'source': State.GAS, 'dest': State.LIQUID},
{'trigger': 'cool', 'source': State.LIQUID, 'dest': State.SOLID}
]
def __init__(self):
super().__init__(states=State, transitions=self.transitions,
initial=State.SOLID, send_event=True)
def subscribe(self, func: Callable, state: State):
self.get_state(state).on_enter.append(func)
def unsubscribe(self, func: Callable, state: State):
self.get_state(state).on_enter.remove(func)
machine = SubscribableMachine()
observer = Observer()
machine.subscribe(observer.state_changed, State.LIQUID)
machine.heat() # >>> state is now 'LIQUID'
machine.heat()
assert machine.state == State.GAS
machine.unsubscribe(observer.state_changed, State.LIQUID)
machine.cool() # no output
assert machine.state == State.LIQUID
我也想要一个 Trigger
的枚举,就像我有一个 State
的枚举一样。
唉,当我尝试
class Trigger(Enum):
heat = auto()
cool = auto()
和
transitions = [
{'trigger': Trigger.heat, 'source': State.SOLID, 'dest': State.LIQUID},
{'trigger': Trigger.heat, 'source': State.LIQUID, 'dest': State.GAS},
{'trigger': Trigger.cool, 'source': State.GAS, 'dest': State.LIQUID},
{'trigger': Trigger.cool, 'source': State.LIQUID, 'dest': State.SOLID}
]
def __init__(self):
super().__init__(states=State, transitions=self.transitions,
initial=State.SOLID, send_event=True)
我明白了
Traceback (most recent call last):
File "C:/code/EPMD/Kodex/Algorithms/src/python/epmd/ablation_points/queries/dfgfdsg.py", line 42, in <module>
machine = SubscribableMachine()
File "C:/code/EPMD/Kodex/Algorithms/src/python/epmd/ablation_points/queries/dfgfdsg.py", line 33, in __init__
initial=State.SOLID, send_event=True)
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 589, in __init__
self.add_model(model)
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 607, in add_model
self._add_trigger_to_model(trigger, mod)
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 813, in _add_trigger_to_model
self._checked_assignment(model, trigger, partial(self.events[trigger].trigger, model))
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 807, in _checked_assignment
if hasattr(model, name):
TypeError: hasattr(): attribute name must be string
我可以用Enum
的.name
:
解决
transitions = [
{'trigger': Trigger.heat.name, 'source': State.SOLID, 'dest': State.LIQUID},
{'trigger': Trigger.heat.name, 'source': State.LIQUID, 'dest': State.GAS},
{'trigger': Trigger.cool.name, 'source': State.GAS, 'dest': State.LIQUID},
{'trigger': Trigger.cool.name, 'source': State.LIQUID, 'dest': State.SOLID}
]
但是 State
和 Trigger
之间的不对称困扰着我。
难道我做错了什么?为什么枚举适用于 State
而不是 Trigger
?
错误说明正确:
TypeError: hasattr(): attribute name must be string
。如果您浏览枚举文档 (https://docs.python.org/3/library/enum.html),您将看到每个函数的内容 return。您可以覆盖它们并查看是否有效。除非 Machine
中的代码专门检查 Enum
并将其转换为字符串,否则它不会工作。
使其工作的更简单方法,以您希望的方式执行以下操作:
class Trigger:
HEAT = 'HEAT'
COOL = 'COOL'
正如已经指出的那样,没有办法直接使用枚举作为触发器。 Transitions 将触发器名称作为方法绑定到模型,因此要求触发器是字符串。但是,Machine
和所有子class 都是在编写时考虑到继承的。如果你想使用枚举而不是classes和class属性,你可以派生一个合适的EnumTransitionMachine
并得到一个统一的接口:
from transitions import Machine
from enum import Enum, auto
class State(Enum):
A = auto()
B = auto()
C = auto()
class Transitions(Enum):
GO = auto()
PROCEED = auto()
class EnumTransitionMachine(Machine):
def add_transition(self, trigger, *args, **kwargs):
super().add_transition(trigger.name.lower() if hasattr(trigger, 'name') else trigger, *args, **kwargs)
transitions = [[Transitions.GO, State.A, State.B], [Transitions.PROCEED, State.B, State.C]]
m = EnumTransitionMachine(states=State, transitions=transitions, initial=State.A)
m.go()
assert m.state == State.B
m.proceed()
assert m.is_C()
仅供参考:也可以将 Enums
与字符串值一起使用:
class State(Enum):
A = "A"
B = "B"
C = "C"
class Transitions(Enum):
GO = "GO"
PROCEED = "PROCEED"
# you could use the enum's value then instead:
def add_transition(self, trigger, *args, **kwargs):
super().add_transition(trigger.value.lower() if hasattr(trigger, 'value') else trigger, *args, **kwargs)
作为
from transitions import Machine
from transitions import EventData
from typing import Callable
from enum import Enum, auto
class Observer:
def state_changed(self, event_data: EventData):
print(f"state is now '{event_data.state.name}'")
class State(Enum):
SOLID = auto()
LIQUID = auto()
GAS = auto()
class SubscribableMachine(Machine):
transitions = [
{'trigger': 'heat', 'source': State.SOLID, 'dest': State.LIQUID},
{'trigger': 'heat', 'source': State.LIQUID, 'dest': State.GAS},
{'trigger': 'cool', 'source': State.GAS, 'dest': State.LIQUID},
{'trigger': 'cool', 'source': State.LIQUID, 'dest': State.SOLID}
]
def __init__(self):
super().__init__(states=State, transitions=self.transitions,
initial=State.SOLID, send_event=True)
def subscribe(self, func: Callable, state: State):
self.get_state(state).on_enter.append(func)
def unsubscribe(self, func: Callable, state: State):
self.get_state(state).on_enter.remove(func)
machine = SubscribableMachine()
observer = Observer()
machine.subscribe(observer.state_changed, State.LIQUID)
machine.heat() # >>> state is now 'LIQUID'
machine.heat()
assert machine.state == State.GAS
machine.unsubscribe(observer.state_changed, State.LIQUID)
machine.cool() # no output
assert machine.state == State.LIQUID
我也想要一个 Trigger
的枚举,就像我有一个 State
的枚举一样。
唉,当我尝试
class Trigger(Enum):
heat = auto()
cool = auto()
和
transitions = [
{'trigger': Trigger.heat, 'source': State.SOLID, 'dest': State.LIQUID},
{'trigger': Trigger.heat, 'source': State.LIQUID, 'dest': State.GAS},
{'trigger': Trigger.cool, 'source': State.GAS, 'dest': State.LIQUID},
{'trigger': Trigger.cool, 'source': State.LIQUID, 'dest': State.SOLID}
]
def __init__(self):
super().__init__(states=State, transitions=self.transitions,
initial=State.SOLID, send_event=True)
我明白了
Traceback (most recent call last):
File "C:/code/EPMD/Kodex/Algorithms/src/python/epmd/ablation_points/queries/dfgfdsg.py", line 42, in <module>
machine = SubscribableMachine()
File "C:/code/EPMD/Kodex/Algorithms/src/python/epmd/ablation_points/queries/dfgfdsg.py", line 33, in __init__
initial=State.SOLID, send_event=True)
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 589, in __init__
self.add_model(model)
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 607, in add_model
self._add_trigger_to_model(trigger, mod)
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 813, in _add_trigger_to_model
self._checked_assignment(model, trigger, partial(self.events[trigger].trigger, model))
File "C:\Code\EPMD\Kodex\EPD_Prerequisite\python_3.7.6\Lib\site-packages\transitions\core.py", line 807, in _checked_assignment
if hasattr(model, name):
TypeError: hasattr(): attribute name must be string
我可以用Enum
的.name
:
transitions = [
{'trigger': Trigger.heat.name, 'source': State.SOLID, 'dest': State.LIQUID},
{'trigger': Trigger.heat.name, 'source': State.LIQUID, 'dest': State.GAS},
{'trigger': Trigger.cool.name, 'source': State.GAS, 'dest': State.LIQUID},
{'trigger': Trigger.cool.name, 'source': State.LIQUID, 'dest': State.SOLID}
]
但是 State
和 Trigger
之间的不对称困扰着我。
难道我做错了什么?为什么枚举适用于 State
而不是 Trigger
?
错误说明正确:
TypeError: hasattr(): attribute name must be string
。如果您浏览枚举文档 (https://docs.python.org/3/library/enum.html),您将看到每个函数的内容 return。您可以覆盖它们并查看是否有效。除非 Machine
中的代码专门检查 Enum
并将其转换为字符串,否则它不会工作。
使其工作的更简单方法,以您希望的方式执行以下操作:
class Trigger:
HEAT = 'HEAT'
COOL = 'COOL'
正如Machine
和所有子class 都是在编写时考虑到继承的。如果你想使用枚举而不是classes和class属性,你可以派生一个合适的EnumTransitionMachine
并得到一个统一的接口:
from transitions import Machine
from enum import Enum, auto
class State(Enum):
A = auto()
B = auto()
C = auto()
class Transitions(Enum):
GO = auto()
PROCEED = auto()
class EnumTransitionMachine(Machine):
def add_transition(self, trigger, *args, **kwargs):
super().add_transition(trigger.name.lower() if hasattr(trigger, 'name') else trigger, *args, **kwargs)
transitions = [[Transitions.GO, State.A, State.B], [Transitions.PROCEED, State.B, State.C]]
m = EnumTransitionMachine(states=State, transitions=transitions, initial=State.A)
m.go()
assert m.state == State.B
m.proceed()
assert m.is_C()
仅供参考:也可以将 Enums
与字符串值一起使用:
class State(Enum):
A = "A"
B = "B"
C = "C"
class Transitions(Enum):
GO = "GO"
PROCEED = "PROCEED"
# you could use the enum's value then instead:
def add_transition(self, trigger, *args, **kwargs):
super().add_transition(trigger.value.lower() if hasattr(trigger, 'value') else trigger, *args, **kwargs)