2017-03-02 52 views
2

我使用Transitions,这是一个非常有用的用于python的FSM工具。我想让这些状态变得更加呃有状态......这样变量对于状态就可以是局部的,并且当状态进入或离开时它们的值会改变。我在机器中得到了相当数量的实例变量 - 我真的很想在状态中使用其中的一些值(例如,我一直处于这种状态多长时间)。它们不是模型的属性,它们是通过状态进展的属性。在转换中制作“状态”fsm package更“有状态”

我不知道是否有'最好的办法'来做到这一点?子类状态?

感谢

回答

1

我不知道的“最佳途径”,但一个合理的方法取决于你想要达到的目标。你可以a)子类状态,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

+0

这不是状态局部变量的通常解释。你提出的是持久性的变量:当你进入一个状态时,它仍然和上次一样具有相同的变量。通常情况下,你需要将变量放入一个状态,以确保每次进入状态时都会对它们进行新的初始化,而这种解决方案**不会。我不确定这是OP的想法,但我认为我应该评论,因为这个建议让我完全错过了错误的胡同。 –

+0

@DanHule:好评。在转换中,状态实例是持久的。 OP声明“...,当状态进入或离开时,它们的值改变了**。”(这可能是错误地)暗示我不认为这些属性是范围/时间属性。对于* local/temporal/scoped *状态[this](https://github.com/aleneum/rsbhsm/blob/master/rsbhsm/rsbhsm.py#L99)可能会有所帮助。每次输入状态时,都可以传递一个类或类路径,用于初始化对象。 – aleneum