如何在pytransition的条件函数中传递事件参数
How to pass event parameters in condition function in pytransition
我正在尝试将参数传递给条件。但它给出了以下错误
代码:
from transitions import Machine
class evenodd(object):
def get_no(self, event):
self.no = event.kwargs.get('no', 0)
def calc_mod(self, event):
self.mod = event.kwargs.get('mod', self.no%2)
def is_even(self, event):
if self.mod == 0:
return True
obj = evenodd()
machine = Machine(obj, ['init', 'getno', 'even', 'odd'], send_event=True, initial='init', ignore_invalid_triggers=True, auto_transitions=False)
machine.add_transition('enterno', 'init', 'getno', before='get_no')
machine.add_transition('isevenodd', 'getno', 'even', before='calc_mod', conditions=['is_even'])
machine.add_transition('isevenodd', 'getno', 'odd', before='calc_mod', conditions=['is_odd'])
s_state = obj.state
print("state --> "+s_state)
trigger = machine.get_triggers(s_state)[0]
print("transition --> "+trigger)
obj.enterno(2)
s_state = obj.state
print("state --> "+s_state)
trigger = machine.get_triggers(s_state)[0]
print("transition --> "+trigger)
obj.isevenodd()
s_state = obj.state
print("state --> "+s_state)
错误:
state --> init
transition --> enterno
state --> getno
transition --> isevenodd
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-75-d8ede3775dc5> in <module>
8 trigger = machine.get_triggers(s_state)[0]
9 print("transition --> "+trigger)
---> 10 obj.isevenodd()
11 s_state = obj.state
12 print("state --> "+s_state)
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in trigger(self, model, *args, **kwargs)
388 # Machine._process should not be called somewhere else. That's why it should not be exposed
389 # to Machine users.
--> 390 return self.machine._process(func)
391
392 def _trigger(self, model, *args, **kwargs):
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _process(self, trigger)
1112 if not self._transition_queue:
1113 # if trigger raises an Error, it has to be handled by the Machine.process caller
-> 1114 return trigger()
1115 else:
1116 raise MachineError("Attempt to process events synchronously while transition queue is not empty!")
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _trigger(self, model, *args, **kwargs)
406 raise MachineError(msg)
407 event_data = EventData(state, self, self.machine, model, args=args, kwargs=kwargs)
--> 408 return self._process(event_data)
409
410 def _process(self, event_data):
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _process(self, event_data)
415 for trans in self.transitions[event_data.state.name]:
416 event_data.transition = trans
--> 417 if trans.execute(event_data):
418 event_data.result = True
419 break
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in execute(self, event_data)
260 _LOGGER.debug("%sExecuted callbacks before conditions.", event_data.machine.name)
261
--> 262 if not self._eval_conditions(event_data):
263 return False
264
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _eval_conditions(self, event_data)
241 def _eval_conditions(self, event_data):
242 for cond in self.conditions:
--> 243 if not cond.check(event_data):
244 _LOGGER.debug("%sTransition condition failed: %s() does not return %s. Transition halted.",
245 event_data.machine.name, cond.func, cond.target)
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in check(self, event_data)
179 predicate = event_data.machine.resolve_callable(self.func, event_data)
180 if event_data.machine.send_event:
--> 181 return predicate(event_data) == self.target
182 return predicate(*event_data.args, **event_data.kwargs) == self.target
183
<ipython-input-74-5dc86072aabd> in is_even(self, event)
7 def is_even(self, event):
8 """ Basically a coin toss. """
----> 9 if self.mod == 0:
10 return True
11
AttributeError: 'evenodd' object has no attribute 'mod'
如何与每个回调共享变量 no 和 mod。我尝试使用该事件。
在这个简单的示例中,我尝试创建一个状态机以根据给定输入是偶数还是奇数来达到状态。
您可以共享变量作为模型的属性。不过,您需要考虑回调执行顺序。根据 transitions
documentation 回调按以下顺序执行:
'machine.prepare_event'
'transition.prepare'
'transition.conditions'
'transition.unless'
'machine.before_state_change'
'transition.before'
'state.on_exit'
<STATE CHANGE>
'state.on_enter'
'transition.after'
'machine.after_state_change'
'machine.finalize_event'
您可以看到 在 条件被评估后触发 before
并且转换 肯定会 发生。因此,self.mod
在 is_even
中不可用,因为 calc_mod
尚未被调用。该列表还显示了如何处理此问题:Execute calc_mod
in prepare
instead of before
:
machine.add_transition('isevenodd', 'getno', 'even', prepare='calc_mod', conditions=['is_even'])
machine.add_transition('isevenodd', 'getno', 'odd', prepare='calc_mod', conditions=['is_odd'])
prepare
正是针对条件检查需要一些设置的用例引入的。如果还需要 'tear down',则可以使用 machine.finalize_event
,无论是否发生转换,它都会被调用。说到回调解析顺序:您可以将条件检查传递给 unless
,如果它们的计算结果为真,这将停止转换。您可以将 conditions='is_odd'
替换为 unless='is_even'
.
如果您不介意某些隐式逻辑,您可以完全放弃 'is_odd' 检查。一组有效的触发器总是按照它们被添加的顺序进行评估。这意味着只有当 'getno' -> 'even'
被发现无效时才会考虑 'getno' -> 'odd'
。 Design-wise,添加 last 的无条件转换将充当 'else' 子句,当无法满足先前的条件集时将执行该子句。
如何将变量传递给回调
一种方法是在机器上处理 send_event=True
时传递给回调的 event
对象。您还可以将变量作为触发器参数传递:
from transitions import Machine
class evenodd(object):
def get_no(self, no=0, **kwargs):
self.no = no
def calc_mod(self, mod=None, **kwargs):
self.mod = mod if mod else self.no % 2
def is_even(self):
return self.mod == 0
obj = evenodd()
machine = Machine(obj, ['init', 'getno', 'even', 'odd'], initial='init',
ignore_invalid_triggers=True, auto_transitions=False)
machine.add_transition('enterno', 'init', 'getno', before='get_no')
machine.add_transition('isevenodd', 'getno', 'even', prepare='calc_mod', conditions=['is_even'])
machine.add_transition('isevenodd', 'getno', 'odd', prepare='calc_mod', conditions=['is_odd'])
obj.enterno(no=2)
obj.isevenodd()
print("state --> " + obj.state)
如果这样做,您必须确保每个回调都可以使用向它们抛出的所有 参数。使用 kwargs
可以很容易地使用 'discard' 不需要的参数。我还建议 always 将参数作为关键字参数传递,即使也支持位置参数。根据我的经验,这使代码更易于理解(例如更易于阅读方法定义)并减少错误源(例如错误的参数映射)。
我正在尝试将参数传递给条件。但它给出了以下错误
代码:
from transitions import Machine
class evenodd(object):
def get_no(self, event):
self.no = event.kwargs.get('no', 0)
def calc_mod(self, event):
self.mod = event.kwargs.get('mod', self.no%2)
def is_even(self, event):
if self.mod == 0:
return True
obj = evenodd()
machine = Machine(obj, ['init', 'getno', 'even', 'odd'], send_event=True, initial='init', ignore_invalid_triggers=True, auto_transitions=False)
machine.add_transition('enterno', 'init', 'getno', before='get_no')
machine.add_transition('isevenodd', 'getno', 'even', before='calc_mod', conditions=['is_even'])
machine.add_transition('isevenodd', 'getno', 'odd', before='calc_mod', conditions=['is_odd'])
s_state = obj.state
print("state --> "+s_state)
trigger = machine.get_triggers(s_state)[0]
print("transition --> "+trigger)
obj.enterno(2)
s_state = obj.state
print("state --> "+s_state)
trigger = machine.get_triggers(s_state)[0]
print("transition --> "+trigger)
obj.isevenodd()
s_state = obj.state
print("state --> "+s_state)
错误:
state --> init
transition --> enterno
state --> getno
transition --> isevenodd
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-75-d8ede3775dc5> in <module>
8 trigger = machine.get_triggers(s_state)[0]
9 print("transition --> "+trigger)
---> 10 obj.isevenodd()
11 s_state = obj.state
12 print("state --> "+s_state)
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in trigger(self, model, *args, **kwargs)
388 # Machine._process should not be called somewhere else. That's why it should not be exposed
389 # to Machine users.
--> 390 return self.machine._process(func)
391
392 def _trigger(self, model, *args, **kwargs):
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _process(self, trigger)
1112 if not self._transition_queue:
1113 # if trigger raises an Error, it has to be handled by the Machine.process caller
-> 1114 return trigger()
1115 else:
1116 raise MachineError("Attempt to process events synchronously while transition queue is not empty!")
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _trigger(self, model, *args, **kwargs)
406 raise MachineError(msg)
407 event_data = EventData(state, self, self.machine, model, args=args, kwargs=kwargs)
--> 408 return self._process(event_data)
409
410 def _process(self, event_data):
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _process(self, event_data)
415 for trans in self.transitions[event_data.state.name]:
416 event_data.transition = trans
--> 417 if trans.execute(event_data):
418 event_data.result = True
419 break
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in execute(self, event_data)
260 _LOGGER.debug("%sExecuted callbacks before conditions.", event_data.machine.name)
261
--> 262 if not self._eval_conditions(event_data):
263 return False
264
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in _eval_conditions(self, event_data)
241 def _eval_conditions(self, event_data):
242 for cond in self.conditions:
--> 243 if not cond.check(event_data):
244 _LOGGER.debug("%sTransition condition failed: %s() does not return %s. Transition halted.",
245 event_data.machine.name, cond.func, cond.target)
~\Anaconda3\envs\ml_nlp_cpu\lib\site-packages\transitions\core.py in check(self, event_data)
179 predicate = event_data.machine.resolve_callable(self.func, event_data)
180 if event_data.machine.send_event:
--> 181 return predicate(event_data) == self.target
182 return predicate(*event_data.args, **event_data.kwargs) == self.target
183
<ipython-input-74-5dc86072aabd> in is_even(self, event)
7 def is_even(self, event):
8 """ Basically a coin toss. """
----> 9 if self.mod == 0:
10 return True
11
AttributeError: 'evenodd' object has no attribute 'mod'
如何与每个回调共享变量 no 和 mod。我尝试使用该事件。 在这个简单的示例中,我尝试创建一个状态机以根据给定输入是偶数还是奇数来达到状态。
您可以共享变量作为模型的属性。不过,您需要考虑回调执行顺序。根据 transitions
documentation 回调按以下顺序执行:
'machine.prepare_event'
'transition.prepare'
'transition.conditions'
'transition.unless'
'machine.before_state_change'
'transition.before'
'state.on_exit'
<STATE CHANGE>
'state.on_enter'
'transition.after'
'machine.after_state_change'
'machine.finalize_event'
您可以看到 在 条件被评估后触发 before
并且转换 肯定会 发生。因此,self.mod
在 is_even
中不可用,因为 calc_mod
尚未被调用。该列表还显示了如何处理此问题:Execute calc_mod
in prepare
instead of before
:
machine.add_transition('isevenodd', 'getno', 'even', prepare='calc_mod', conditions=['is_even'])
machine.add_transition('isevenodd', 'getno', 'odd', prepare='calc_mod', conditions=['is_odd'])
prepare
正是针对条件检查需要一些设置的用例引入的。如果还需要 'tear down',则可以使用 machine.finalize_event
,无论是否发生转换,它都会被调用。说到回调解析顺序:您可以将条件检查传递给 unless
,如果它们的计算结果为真,这将停止转换。您可以将 conditions='is_odd'
替换为 unless='is_even'
.
如果您不介意某些隐式逻辑,您可以完全放弃 'is_odd' 检查。一组有效的触发器总是按照它们被添加的顺序进行评估。这意味着只有当 'getno' -> 'even'
被发现无效时才会考虑 'getno' -> 'odd'
。 Design-wise,添加 last 的无条件转换将充当 'else' 子句,当无法满足先前的条件集时将执行该子句。
如何将变量传递给回调
一种方法是在机器上处理 send_event=True
时传递给回调的 event
对象。您还可以将变量作为触发器参数传递:
from transitions import Machine
class evenodd(object):
def get_no(self, no=0, **kwargs):
self.no = no
def calc_mod(self, mod=None, **kwargs):
self.mod = mod if mod else self.no % 2
def is_even(self):
return self.mod == 0
obj = evenodd()
machine = Machine(obj, ['init', 'getno', 'even', 'odd'], initial='init',
ignore_invalid_triggers=True, auto_transitions=False)
machine.add_transition('enterno', 'init', 'getno', before='get_no')
machine.add_transition('isevenodd', 'getno', 'even', prepare='calc_mod', conditions=['is_even'])
machine.add_transition('isevenodd', 'getno', 'odd', prepare='calc_mod', conditions=['is_odd'])
obj.enterno(no=2)
obj.isevenodd()
print("state --> " + obj.state)
如果这样做,您必须确保每个回调都可以使用向它们抛出的所有 参数。使用 kwargs
可以很容易地使用 'discard' 不需要的参数。我还建议 always 将参数作为关键字参数传递,即使也支持位置参数。根据我的经验,这使代码更易于理解(例如更易于阅读方法定义)并减少错误源(例如错误的参数映射)。