【问题标题】:How to pass event parameters in condition function in pytransition如何在pytransition的条件函数中传递事件参数
【发布时间】:2020-08-11 13:16:54
【问题描述】:

我正在尝试将参数传递给条件。但它给出了以下错误

代码:

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。我尝试使用该事件。 在这个简单的示例中,我尝试创建一个状态机,以根据给定输入是偶数还是奇数来达到状态。

【问题讨论】:

    标签: python transition fsm pytransitions


    【解决方案1】:

    您可以共享变量作为模型的属性。不过,您需要考虑回调执行顺序。根据transitionsdocumentation,回调按此顺序执行:

    • 'machine.prepare_event'
    • 'transition.prepare'
    • 'transition.conditions'
    • 'transition.unless'
    • 'machine.before_state_change'
    • 'transition.before'
    • 'state.on_exit'
    • &lt;STATE CHANGE&gt;
    • 'state.on_enter'
    • 'transition.after'
    • 'machine.after_state_change'
    • 'machine.finalize_event'

    您可以看到before 被触发条件已经评估并且转换一定会发生。因此,self.modis_even 中不可用,因为尚未调用 calc_mod。该列表还显示了如何处理此问题:在prepare 中执行calc_mod 而不是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 专门针对条件检查需要一些设置的用例引入。如果还需要“拆除”,您可以使用machine.finalize_event,无论是否发生转换,它都会被调用。说到回调解决顺序:您可以将条件检查传递给unless,如果它们评估为真,它将停止转换。您可以将conditions='is_odd' 替换为unless='is_even'

    如果您不介意一些隐含的逻辑,您可以完全放弃“is_odd”检查。一组有效触发器总是按照它们添加的顺序进行评估。这意味着只有当'getno' -&gt; 'even' 被发现无效时才会考虑'getno' -&gt; 'odd'。在设计方面,添加 last 的无条件转换将充当“else”子句,当无法满足之前的一组条件时将执行该子句。

    如何将变量传递给回调

    一种方法是在机器上处理event 时传递给回调的send_event=True 对象。您还可以将变量作为触发器参数传递:

    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 可以很容易地“丢弃”不需要的参数。我还建议 always 将参数作为关键字参数传递,即使也支持位置参数。以我的经验,这使代码更易于理解(例如,更容易阅读方法定义)并减少错误来源(例如错误的参数映射)。

    【讨论】:

      猜你喜欢
      • 2013-05-16
      • 2019-09-01
      • 2013-09-17
      • 2017-07-03
      • 2011-10-06
      • 1970-01-01
      • 2013-10-15
      • 1970-01-01
      相关资源
      最近更新 更多