在过渡中制作“状态”fsm包装更“有状态”

时间:2017-03-02 02:02:21

标签: python transitions

我正在使用Transitions,这是一个非常有用的python FSM工具。我想使状态更多,嗯,有状态......这样变量可以是状态的局部变量,并且当输入或离开状态时它们的值会改变。我最终在机器中有相当数量的实例变量 - 我真的想要一些状态中的值(例如,我在这个状态下有多长时间)。它们不是模型的属性,它们是通过状态进展的属性。

我想知道是否有“最好的方法”来做到这一点?子类状态?

感谢

1 个答案:

答案 0 :(得分:1)

我不知道最好的方式'但合理的方法取决于你想要达到的目标。您可以a)子类State,b)修饰初始化状态或c)手动初始化(子类化)状态并将它们传递给机器。

A)如果每个州都有相同的属性,您可以按照建议将子类化为子类:

import transitions.extensions.nesting as nesting


class CounterState(nesting.NestedState):

    def __init__(self, *args, **kwargs):
        super(CounterState, self).__init__(*args, **kwargs)
        self.entered = self.exited = 0

    def enter(self, event_data):
        self.entered += 1

    def exit(self, event_data):
        self.exited += 1

    def __str__(self):
        return "State {0} has been entered {1} times and exited {2} times".format(self.name, self.entered, self.exited)


class CounterMachine(nesting.HierarchicalMachine):

    @staticmethod
    def _create_state(*args, **kwargs):
        return CounterState(*args, **kwargs)

machine = CounterMachine(states=['A', 'B'], initial='A')

a = machine.get_state('A')
b = machine.get_state('B')

print(a) # >>> State A has been entered 0 times and exited 0 times
machine.to_B()
print(a) # >>> State A has been entered 0 times and exited 1 times
print(b) # >>> State B has been entered 1 times and exited 0 times

我在这里使用了NestedMachine,因为到目前为止_create_state中没有Machine更新:从版本0.4.4开始,它也适用于Machine

B)另一种方法是通过模型对启动的状态对象进行一些装饰:

from transitions import Machine


class Model(object):

    def __init__(self):
        self.machine = Machine(model=self, states=['A', 'B'], initial='A', 
                               before_state_change='exit_state',
                               after_state_change='enter_state')
        # loop through all the states and attach attributes
        for state in self.machine.states.values():
            state.entered = 0
            state.exited = 0

    def enter_state(self):
        # retrieve the state object by name
        self.machine.get_state(self.state).entered += 1

    def exit_state(self):
        self.machine.get_state(self.state).exited += 1


def print_state(state):
    print("State {0} has been entered {1} times and exited {2} times".format(state.name, state.entered, state.exited))

m = Model()

a = m.machine.get_state('A')
b = m.machine.get_state('B')

print_state(a)
m.to_B()
print_state(a)
print_state(b)

C)如果必须单独处理每个州,您可以手动启动状态并将实例传递给机器而不是名称:

from transitions import Machine, State


class TicketState(State):

    def __init__(self, name, tickets):
        super(TicketState, self).__init__(name)
        self.tickets = tickets


class Model(object):

    def __init__(self):

        # Using our own state
        a = TicketState('A', 10)

        # Setting tickets ourselves
        b = State('B')
        b.tickets = 3

        self.machine = Machine(self, states=[a, b], initial='A',
                               before_state_change='decrease_tickets')

    def tickets_left(self):
        return self.machine.get_state(self.state).tickets > 0

    def decrease_tickets(self):
        s = self.machine.get_state(self.state)
        s.tickets -= 1
        if s.tickets < 0:
            raise Exception('No Tickets left!')
        print("State {0} has {1} tickets left.".format(s.name, s.tickets))

m = Model()
m.to_B() # >>> State A has 9 tickets left.
m.to_A() # >>> State B has 2 tickets left.

当然,属性和名称的数量可能有所不同。您也可以将before_state_change回调传递给on_enter/exit对象,以便在转换期间单独处理每个状态,而不是使用机器回调State。或者,如果您只需要一组不同的州类型,例如State.enter(self, event_data)和/或TimedState,则为子类CounterState