2017-03-02 9 views
2

私はTransitionsを使用しています。これはPython用の非常に便利なFSMツールです。状態をもっと覚えて、ステートフルにしたいと思っています...変数が状態に対して局所的であり、状態が入力または放置されたときに値が変化するようにします。かなりの数のインスタンス変数がマシンに残っています。私は本当に状態の値のいくつかを望んでいます(例えば、この状態にどれくらいの期間入っているか)。それらはモデルの属性ではなく、状態を通した進歩の属性です。遷移中の "状態"をfsmパッケージの "ステートフル"にする

これを行うための「最良の方法」があるのだろうか?サブクラスの状態?

おかげ

答えて

1

は、私が「最良の方法」を認識していないですが、合理的なアプローチは、あなたが達成したいかに依存します。 a)状態のサブクラス化、b)初期化された状態の修飾、またはc)手動での初期化(サブクラス化)された状態を機械に渡すことができます。すべての状態は同じ属性を持っている場合は、あなたが示唆したよう

A)あなたは状態をサブクラス化することができます_create_stateは、これまでMachineでは利用できないので、私はここにNestedMachineを使用していた

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 

更新:バージョン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オブジェクトに渡して、遷移中に各状態を個別に処理することもできます。またはTimedStateCounterStateのような異なる状態タイプのセットが必要な場合は、State.enter(self, event_data)をサブクラス化してください。

+0

これは、状態ローカル変数の通常の解釈ではありません。あなたが提案しているのは、永続的な変数です。状態を入力すると、前回と同じ変数が残っています。通常は、変数を状態に入れて、状態が入力されるたびに変数が新しく初期化されるようにします。これは**この方法ではできません**。私はそれがOPが望んでいたかどうか分からないが、この提案が私に間違った路地を完全に導いたので、私はコメントしなければならないと思った。 –

+0

@DanHule:良い発言。トランジションでは、状態インスタンスは永続的です。 OPは "...、状態が入力されたとき、またはその値が**変化した。**"ということは、スコープ/時間属性ではないことを(私が誤って)暗示している可能性があります。 * local/temporal/scoped *州については、[this](https://github.com/aleneum/rsbhsm/blob/master/rsbhsm/rsbhsm.py#L99)が参考になるかもしれません。状態が入力されるたびに、オブジェクトを初期化するために使用されるクラスまたはクラスパスを渡すことができます。 – aleneum

関連する問題