0

I wrote a StateMachine class in Python so it could be inherited from. The logic of it work as expected but I can access the Attribute self.data from a State which is present in the derived StateMachine class named StateMachineTest.

The following error is generated:

Traceback (most recent call last):
File "/home/nbout/work/python/state_machine/main.py", line 68, in 
   main()
File "/home/nbout/work/python/state_machine/main.py", line 59, in main
   test = StateMachineTest()
File "/home/nbout/work/python/state_machine/main.py", line 47, in __init__
   StateMachine.__init__(self, Started())
File "/home/nbout/work/python/state_machine/state_machine.py", line 17, in _init__
   self.current_state.on_enter(self)
File "/home/nbout/work/python/state_machine/main.py", line 16, in on_enter
   print("Started: data:{}".format(sm_test.data))
Started: on_enter
AttributeError: 'StateMachineTest' object has no attribute 'data'
Started: on_exit

state_machine.py

class State:

    def on_enter(self, state_machine):
        pass

    def on_exit(self, state_machine):
        pass


class StateMachine:

    def __init__(self, start_state):
        self.current_state = start_state

        self.current_state.on_enter(self)

    def __del__(self):
        self.current_state.on_exit(self)

    def set_state(self, state):

        self.current_state.on_exit(self)
        self.current_state = state
        self.current_state.on_enter(self)

main.py

from state_machine import StateMachine
from state_machine import State


class StateTest(State):
    def pause(self, state_machine_test):
        pass

    def start(self, state_machine_test):
        pass


class Started(StateTest):
    def on_enter(self, sm_test):
        print("Started: on_enter")
        print("Started: data:{}".format(sm_test.data))

    def on_exit(self, sm_test):
        print("Started: on_exit")

    def pause(self, sm_test):
        print("Started: pause")
        sm_test.set_state(Paused())

    def start(self, sm_test):
        print("Started: start")


class Paused(StateTest):
    def on_enter(self, sm_test):
        print("Paused: on_enter")

    def on_exit(self, sm_test):
        print("Paused: on_exit")

    def pause(self, sm_test):
        print("Paused: pause")

    def start(self, sm_test):
        print("Paused: start")
        sm_test.set_state(Started())


class StateMachineTest(StateMachine):

    def __init__(self):
        StateMachine.__init__(self, Started())
        self.data = 10

    def pause(self):
        self.current_state.pause(self)

    def start(self):
        self.current_state.start(self)


def main():

    test = StateMachineTest()

    test.start()
    test.pause()
    test.pause()
    test.start()


if __name__ == '__main__':
    main()

2 Answers 2

0

Your problem lies in the fact that StateMachine.__init__(self, Started()) requires self's data atttribute to already be set, but you only set it the next line (self.data = 10). Switch these two lines around and try again!

Sign up to request clarification or add additional context in comments.

1 Comment

This is the solution, thanks. I will Accept your answer as soon as StackOverflow let me...
0

Wow, this code is really unnecessarily complicated.

The problem appears to be that you initialise Started in the __init__ of your StateMachineTest class, so that its on_enter runs before you have set self.data.

You might be able fix this by moving the assignment of self.data above the super call, but a much better bet would be to simplify your inheritance hierarchy.

1 Comment

Thanks, that was my problem. I used inheritance that way to enforced reusability of the StateMachine class

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.