Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/288.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 将转换作为状态机引擎实现状态模式的最佳方法是什么_Python_Transition_State Machine_State Pattern - Fatal编程技术网

Python 将转换作为状态机引擎实现状态模式的最佳方法是什么

Python 将转换作为状态机引擎实现状态模式的最佳方法是什么,python,transition,state-machine,state-pattern,Python,Transition,State Machine,State Pattern,几年前,我实现了一个带有转换的演示应用程序,并尝试使用状态模式。该实现有4个状态机、43个状态和78个转换,但很难理解,并且没有正确地遵循状态模式。我现在需要执行另一个演示,并一直在重新思考如何执行。下面概述了我提议的方法,我想征求关于如何最好地实现状态模式和转换的想法和意见 维基百科在这里描述了状态模式。我的解释是,特定状态下的行为被封装为一个类,并且所有此类类都具有相同的接口 有两个问题需要解决。首先是如何允许回调的目标依赖于状态,其次是如何拥有到StateAction类的单个接口 我提出的

几年前,我实现了一个带有转换的演示应用程序,并尝试使用状态模式。该实现有4个状态机、43个状态和78个转换,但很难理解,并且没有正确地遵循状态模式。我现在需要执行另一个演示,并一直在重新思考如何执行。下面概述了我提议的方法,我想征求关于如何最好地实现状态模式和转换的想法和意见

维基百科在这里描述了状态模式。我的解释是,特定状态下的行为被封装为一个类,并且所有此类类都具有相同的接口

有两个问题需要解决。首先是如何允许回调的目标依赖于状态,其次是如何拥有到StateAction类的单个接口

我提出的解决这两个问题的方法如下:-

  • 将StateAction类的接口定义为单个方法
    def dispatch(self、trigger、*args、**kwargs)
  • 创建一组StateAction类,每个类对状态中的行为进行编码。(注意这些不是transition.State类)
  • 在模型中,在dict中实例化所有这些StateAction类,并键入状态名称
  • 跳过
    机器。解析\u callable
    ,以便回调调用当前状态下的分派方法,并将触发器作为参数
  • 每个实例中的分派方法在self上调用适当的方法
新Machine.resolve_callable需要引用模型中实例的dict。这是通过对Machine类进行子类化并添加一个
actions=xxx
关键字项来实现的

所有回调都成为对当前状态实例上具有合适触发器参数的dispatch方法的调用。比如说

transitions = [
    { 'trigger': 'melt', 'source': 'solid', 'dest': 'liquid', 'before': 'make_hissing_noises'},
]
将导致调用
solid.dispatch('make\u hissing\u noises')
,其中solid是StateAction类的实例

此方法不支持机器附加到模型的各种方便方法。这对我来说不是问题,因为我的应用程序基于事件循环,并且总是使用调用
model.trigger(xxx)
来启动状态更改。这对其他人来说可能不是问题,因为如果不同的方法在不同的类上,人们不会期望在模型上有类似的方法

下面的代码用一个简单的示例说明了该方法,其中它为当前状态调用
on\u Train\u arrized
方法,即使该方法存在于两个StateAction实例上

from transitions import Machine
from functools import partial
from six import string_types
import logging
logging.basicConfig(level=logging.WARNING)

class StatePatternMachine(Machine):
    """Machine that simplifies using the State Pattern"""
    
    def __init__(self, *args, **kwargs):
        self._actions = kwargs.pop('actions')
        super().__init__(*args, **kwargs)

    @staticmethod
    def resolve_callable(target, event_data):
        """ Converts a model's property name into a call to dispatch.
            The target must be a string.
        Args:
            target (str): method name
            event_data (EventData): Currently processed event
        Returns:
            callable function dispatch on current state
        """
        if not isinstance(target, string_types):
            raise ValueError('%r is not be a string' % target)
        else:
            try:
                func = getattr(event_data.machine._actions[event_data.state.name], 'dispatch')
            except AttributeError:
                raise AttributeError('No dispatch method on %r' % event_data.model.states)
        return partial(func, target) # dispatch signature is dispatch(trigger, *args, **kwargs)


class StateAction(object):
    """base class for all classes implementing the State Pattern"""
    
    def dispatch(self, trigger, *args, **kwargs):
        """call the appropriate function on self
            this is the common interface to all state classes
        """
        func = getattr(self, trigger, None)
        if func:
            return func(*args, **kwargs)
        else:
            print(f'have not got {trigger}')

            # simple model for a set of states implementing the State Pattern
class BuyingTicket(StateAction):
    _name = 'BuyingTicket'
    def on_Train_Arrived(self, *args, **kwargs):
        print('Missed Train')
    def on_Train_Cancelled(self, *args, **kwargs):
        print('on_Train_Cancelled')

class Waiting(StateAction):
    _name = 'Waiting'
    def on_Train_Arrived(self, *args, **kwargs):
        print('Caught Train')
    def on_Timeout(self, *args, **kwargs):
        print('Timeout')

class Home(StateAction):
    _name = 'Home'

journey_states = [
    BuyingTicket,
    Waiting,
    Home,
]
transitions = [
    {'trigger': 'Train_Arrived', 'source': 'BuyingTicket', 'dest': 'Waiting', 'before': 'on_Train_Arrived'}, 
    {'trigger': 'Train_Cancelled', 'source': 'BuyingTicket', 'dest': 'Home', 'before': 'on_Train_Cancelled'}, 
    {'trigger': 'Train_Arrived', 'source': 'Waiting', 'dest': 'Home', 'before': 'on_Train_Arrived'}
]
class Model(object):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # dict of instances of states
        self.states = {s._name: s() for s in journey_states}

        self.machine = StatePatternMachine(
            model=self,
            states=[k for k in self.states],
            transitions=transitions,
            initial='BuyingTicket',
            actions=self.states,
        )

model = Model()
model.trigger('Train_Arrived')

Missed Train
True