如何将参数传递给`transitions` 库中的 on_enter 回调?

问题描述

我想使用 transitions,需要一个我在文档中找不到的相当琐碎的功能,我想知道它是否已实现:

我想在某个状态上定义一个 DOCKER_TAG := .. 回调,但要向该回调传递一个参数。至少要知道我是从哪个州进入该州。

来自文档:

on_enter

我缺少的是

class Matter(object):
    def say_hello(self): print("hello,new state!")
    def say_goodbye(self): print("goodbye,old state!")

lump = Matter()

# Same states as above,but Now we give StateA an exit callback
states = [
    State(name='solid',on_exit=['say_goodbye']),'liquid',{ 'name': 'gas','on_exit': ['say_goodbye']}
    ]

machine = Machine(lump,states=states)
machine.add_transition('sublimate','solid','gas')

# Callbacks can also be added after initialization using
# the dynamically added on_enter_ and on_exit_ methods.
# Note that the initial call to add the callback is made
# on the Machine and not on the model.
machine.on_enter_gas('say_hello')

# Test out the callbacks...
machine.set_state('solid')
lump.sublimate()
>>> 'goodbye,old state!'
>>> 'hello,new state!'

这能以某种方式很好地完成吗?

一个明显不好的解决方案是保留一个 def say_hello(self,param): print(f"hello,new state! here is your param: {param}") 参数并自己维护它。
我正在寻找内置的东西。

解决方法

transitions'文档中名为 Passing Data 的部分:

...您可以将任何位置或关键字参数直接传递给触发器方法(在调用 add_transition() 时创建)[...] 您可以将任意数量的参数传递给触发器。这种方法有一个重要的限制:状态转换触发的每个回调函数都必须能够处理所有参数。

对于您的特定示例,这可能如下所示:

from transitions import Machine

class Matter(object):
    def say_hello(self,param):
        print(f"hello,new state! Here is your param: {param}")

    # Every callback MUST be able to process possible callback parameters
    # If parameters are not needed,just use *args and **kwargs in the definition
    def say_goodbye(self,*args):
        print("goodbye,old state!")


lump = Matter()
machine = Machine(lump,states=[{'name': 'solid','on_exit': 'say_goodbye'},'liquid',{'name': 'gas','on_enter': 'say_hello'}],transitions=[['sublimate','solid','gas']],initial='solid')

# pass param as arg
lump.sublimate(lump.state)
# or as kwarg
# lump.sublimate(param=lump.state)

还有第二种方法是通过在 send_event=True 构造函数中传递 Machine 来传递数据。这将改变 transitions 将触发器参数传递给回调的方式:

如果您在机器初始化时设置 send_event=True,触发器的所有参数将被包装在一个 EventData 实例中并传递给每个回调。 (EventData 对象还维护与事件关联的源状态、模型、转换、机器和触发器的内部引用,以防您需要访问这些内容。)

这可能更适合您的用例,因为 EventData 对象还包含有关已执行转换的信息,其中包含源状态的名称:

from transitions import Machine,EventData

class Matter(object):
    def say_hello(self,event: EventData):
        print(f"hello,new state! Here is your param: {event.kwargs['param']}. "
              f"I came here from state '{event.transition.source}'.")

    def say_goodbye(self,event):
        print("goodbye,initial='solid',send_event=True)

lump.sublimate(param=42)