python转换库-在从单一状态转换之前等待多个事件是否可能?

python转换库-在从单一状态转换之前等待多个事件是否可能?,python,transitions,fsm,Python,Transitions,Fsm,github问题的交叉发布 我正在使用python的transitions FSM库,在转换到下一个状态之前,我需要等待3件事情发生 State = WAIT_FOR_3_THINGS Events = thing_1_done, thing_2_done, thing_3_done 事件可以以任何顺序出现,因此我宁愿避免: 完成的事情和完成的事情,完成的事情和完成的事情,完成的事情和完成的事情 我可以跟踪事件1、2和3,并使用 conditions=[thing_1_and_2_done,

github问题的交叉发布

我正在使用python的transitions FSM库,在转换到下一个状态之前,我需要等待3件事情发生

State = WAIT_FOR_3_THINGS
Events = thing_1_done, thing_2_done, thing_3_done
事件可以以任何顺序出现,因此我宁愿避免:

完成的事情和完成的事情
完成的事情和完成的事情
完成的事情和完成的事情

我可以跟踪事件1、2和3,并使用

conditions=[thing_1_and_2_done, thing_1_and_3_done, thing_2_and_3_done]
但我不确定在哪里最好地汇总这些事件的发生


是否有一种更具skookum(ok,pythonic)风格的方法来实现这一点?

最终使用了一个集合来定义我想要在转换到下一个状态之前积累的事件列表

State = WAIT_FOR_3_THINGS
Events = thing_1_done, thing_2_done, thing_3_done
例如:

将实例变量添加到FSM实例以跟踪进入“空闲”状态时清除的实例:

self.run_deps_set = set()
然后,聚合并检查条件子句中的事件子类型:

def if_run_deps_ready(self, event):
    self.run_deps_set.add(event.args[0])
    return self.run_deps_set.issuperset(deps_required)

感觉不太干净,但我也不讨厌它(太多)。

最后使用一个集合来定义我想在转换到下一个状态之前累积的事件列表

State = WAIT_FOR_3_THINGS
Events = thing_1_done, thing_2_done, thing_3_done
例如:

将实例变量添加到FSM实例以跟踪进入“空闲”状态时清除的实例:

self.run_deps_set = set()
然后,聚合并检查条件子句中的事件子类型:

def if_run_deps_ready(self, event):
    self.run_deps_set.add(event.args[0])
    return self.run_deps_set.issuperset(deps_required)

不觉得超级干净,但我不讨厌它(太多)。

< P>不知道这是一个伟大的,甚至是好的解决方案,但是你可以考虑线程或多处理每一件事作为一个函数,包括一个var在每个结束时翻转,当它完成时。

那么,如果一个简单的A完全、B完备、C完备的布尔是真正的检查应该让你到你想去的地方。

< P>不知道这是一个很棒的,甚至是好的解决方案,但是你可以考虑线程或多个处理的每一个事物作为一个函数,并且包括在每一个结尾的var,当它完成时翻转。
那么,一个简单的如果a-完成、b-完成、c-完成的布尔值为真的检查应该会让你达到你想要的目的。

从实用的角度来看,你是过渡的方式,imho。对于这样的问题,并发状态机可能值得一看

对于这种特殊情况,这当然是过分了,但我想借此机会展示转换如何处理并发性。 更准确地说,我们可以使用它处理多个模型的能力来实现并发性。 在下面的示例中,我扩展了
Machine
,使用a)一种方法触发所有模型上的事件,b)一种方法初始化工作模型并将实际模型存储在内部变量中(尽管这不是一个正确的堆栈)。 使用
ignore\u invalid\u触发器
,每个工作进程等待相应的事件,并在事件完成时从
ParallelMachine
模型列表中删除自己。当模型列表为空时,
ParallelMachine
再次恢复实际模型状态

from transitions import Machine


class WorkerModel:

    def __init__(self, machine):
        self.machine = machine

    # I show myself out when I am done
    def on_enter_Done(self):
        self.machine.remove_model(self)


class ParallelMachine(Machine):

    def __init__(self, *args, **kwargs):
        self._pushed_models = []
        super(ParallelMachine, self).__init__(*args, **kwargs)

    # save actual models and initialise worker tasks
    def add_worker(self, tasks):
        self._pushed_models = self.models
        self.models = []
        for t in tasks:
            self.add_model(WorkerModel(self), initial=t)

    # trigger an event on ALL models (either workers or actual models)
    def trigger(self, trigger, *args, **kwargs):
        for m in self.models:
            getattr(m, trigger)(*args, **kwargs)
        if not self.models:
            self.models = self._pushed_models
            self._pushed_models = []
            for m in self.models:
                getattr(m, trigger)(*args, **kwargs)


class Model:

    def __init__(self):
        # the state list contains model states as well as worker states
        states = ['Idle', 'Processing', 'Done', 'need_1', 'need_2', 'need_3']
        # potentially got_1/2/3 all can trigger a transition to 'Done'
        # but the model will only be triggered when the worker list
        # has been emptied before
        transitions = [['process', 'Idle', 'Processing'],
                       ['got_1', ['Processing', 'need_1'], 'Done'],
                       ['got_2', ['Processing', 'need_2'], 'Done'],
                       ['got_3', ['Processing', 'need_3'], 'Done']]
        # assigning machine to a model attribute prevents the need
        # to pass it around
        self.machine = ParallelMachine(model=self, states=states,
                                       transitions=transitions, initial='Idle',
                                       ignore_invalid_triggers=True)

    def on_enter_Processing(self):
        self.machine.add_worker(['need_1', 'need_2', 'need_3'])

    def on_enter_Done(self):
        print("I am done")


model = Model()
machine = model.machine
assert model.state == 'Idle'
# we use ParallelMachine.trigger instead of model.<event_name>
machine.trigger('process')
assert model.state == 'Processing'
machine.trigger('got_3')
machine.trigger('got_1')
# duplicated events do not matter
machine.trigger('got_3')
assert model.state == 'Processing'
machine.trigger('got_2')
# worker stack was empty and 
assert model.state == 'Done'
从导入计算机
类WorkerModel:
定义初始化(自身,机器):
self.machine=机器
#当我完成时,我展示了我自己
def on_输入_完成(自我):
self.machine.remove_模型(self)
类并行机器(机器):
定义初始化(self,*args,**kwargs):
self.__模型=[]
超级(并行机,自).\uuuu初始化(*args,**kwargs)
#保存实际模型并初始化辅助任务
def添加工作人员(自身、任务):
self.\u push\u models=self.models
self.models=[]
对于任务中的t:
self.add_模型(WorkerModel(self),初始值=t)
#在所有模型(工作模型或实际模型)上触发事件
def触发器(自身、触发器、*args、**kwargs):
对于self.models中的m:
getattr(m,触发器)(*args,**kwargs)
如果不是自动模式:
self.models=self.\u模型
self.__模型=[]
对于self.models中的m:
getattr(m,触发器)(*args,**kwargs)
类别模型:
定义初始化(自):
#状态列表包含模型状态和工作状态
状态=['Idle'、'Processing'、'Done'、'need_1'、'need_2'、'need_3']
#潜在的got_1/2/3都可以触发到“完成”的转换
#但是,只有在工作人员列表
#以前都是空的
转换=[['process','Idle','Processing'],
['got_1'、['Processing'、'need_1']、'Done'],
['got_2'、['Processing'、'need_2']、'Done'],
['got_3'、['Processing'、'need_3']、'Done']
#将机器指定给模型属性可以防止需要
#传阅
self.machine=并行机(型号=self,状态=状态,
过渡=过渡,初始值为“空闲”,
忽略(无效的触发器=真)
def on_进入_处理(自):
self.machine.add_worker(['need_1','need_2','need_3']))
def on_输入_完成(自我):
打印(“我完成了”)
模型=模型()
machine=model.machine
断言model.state==“空闲”
#我们使用ParallelMachine.trigger而不是model。
machine.trigger('进程')
assert model.state==“正在处理”
machine.trigger('got_3')
machine.trigger('got_1')
#重复的事件并不重要
machine.trigger('got_3')
assert model.state==“正在处理”
machine.trigger('got_2')
#工作堆栈为空,并且
断言model.state==“完成”
这样,全局状态可以是“处理(需要1/需要2/完成)”或“处理(完成/需要2/完成)”,而无需显式建模所有变量


并发性在任务列表中,但需要进行一些更改。例如,回调预计在所有型号中都可用(例如,从
Model
中删除
on\u enter\u Done
将引发异常)

从v的语用角度