【问题标题】:Key Error: Python Finite State Machine?关键错误:Python 有限状态机?
【发布时间】:2015-04-07 19:22:25
【问题描述】:

这是我在 Python 中简单实现有限状态机的代码。我已经多次运行它无济于事。当各个状态类确实从各个类继承时,它们无法访问运行机器逻辑所需的食物和睡眠变量。我相信我得到了一个 KeyError,因为每次我添加一个新状态时,它都会将当前状态值重置为无。我该如何解决这个错误?有没有更好/正确的方法来组织我的课程?我在 python 方面处于中级水平,但是 OOP 概念对我来说尤其具有挑战性。任何帮助和建议将不胜感激!

class FSM():
    def __init__(self):
        self.states = {}
        self.c_state = None

    def update(self):
        self.states[self.c_state].execute()

    def addState(self, name, handler):
        self.states[name] = handler

    def startState(self, state):
        self.c_state = state

    def changeState(self, newstate):
        print('The current state is:', self.c_state)
        self.states[self.c_state].exit()
        self.c_state = self.states[newstate]
        self.states[self.c_state].enter()

class individual():
    def __init__(self, name, food, sleep):
        self.name = name
        self.food = food
        self.sleep = sleep
        self.fsm = FSM()

class wander(individual):
    def enter(self):
        print('Entering Wander State')

    def execute(self):
        print('WANDERING')
        if self.sleep == 0:
            self.fsm.changeState('SLEEP')
        elif 25 > self.sleep > 0 and self.food > 25:
            self.fsm.changeState('WANDER')
            self.sleep -= 5
            self.food -= 5
        elif self.food < 25:
            self.fsm.changeState('EAT')

    def exit(self):
        print('Leaving Wander State')

class eat(individual):
    def enter(self):
        print('Entering Eating State')

    def execute(self):
        print('EATING')
        if self.sleep > 0 and self.food < 25:
            self.fsm.changeState('EAT')
            self.food += 5
            self.sleep -= 5
        elif self.food > 25 and self.sleep > 0:
            self.fsm.changeState('WANDER')
        elif self.sleep == 0:
            self.fsm.changeState('SLEEP')

    def exit(self):
        print('Exiting Eating State')

class sleep(individual):
    def enter(self):
        print('Entering Sleeping State')

    def execute(self):
        print('SLEEPING')
        if 50 > self.sleep > 0:
            self.fsm.changeState('SLEEP')
            self.sleep += 5
        elif self.sleep == 50 and self.food < 25:
            self.fsm.changeState('EAT')
        elif self.sleep == 50 and self.food > 25:
            self.fsm.changeState('WANDER')

    def exit(self):
        print('Exiting Sleeping State')

aaron = individual('aaron', 10, 30)
aaron.fsm.addState('WANDER', wander(aaron.name, aaron.food, aaron.sleep))
aaron.fsm.addState('EAT', eat(aaron.name, aaron.food, aaron.sleep))
aaron.fsm.addState('SLEEP', sleep(aaron.name, aaron.food, aaron.sleep))
aaron.fsm.startState('WANDER')
print(aaron.fsm.c_state)
aaron.fsm.update()

 File "C:/Users/Aaron/PycharmProjects/InDepthFSM/FSM.py", line 12, in update
    self.states[self.c_state].execute()
  File "C:/Users/Aaron/PycharmProjects/InDepthFSM/FSM.py", line 50, in execute
    self.fsm.changeState('EAT')
  File "C:/Users/Aaron/PycharmProjects/InDepthFSM/FSM.py", line 24, in changeState
    self.states[self.c_state].exit()
KeyError: None

【问题讨论】:

    标签: python oop fsm


    【解决方案1】:

    这里的问题是您在执行更新时引用了不同的 fsm 对象。

    execute 代码 sn-p 中,当您更新状态时,问题是虽然单个对象存在 FSM 对象,但 eat 类不存在它,您尝试更新eat 类的状态。

    # Here, since self refers to the `eat` class, you are creating a new fsm object
    # This means that you are in fact referring to an empty set of states.
    elif self.sleep == 0:
       self.fsm.changeState('SLEEP')
    

    继承并不意味着类共享一个FSM 实例,因此如果您想对属于单个类的FSM 对象进行操作,您需要将个体作为输入传递。

    也就是说,这里的eat 块真的不应该是一个类,而应该是individual 的一个函数,而不是从它继承。例如:

    class individual(object):
        def __init__(self, ...):
            # make object
    
        def eat(self):
            print('EATING')
            if self.sleep > 0 and self.food < 25:
                self.fsm.changeState('EAT')
                self.food += 5
                self.sleep -= 5
            elif self.food > 25 and self.sleep > 0:
                self.fsm.changeState('WANDER')
            elif self.sleep == 0:
                self.fsm.changeState('SLEEP')
    

    在这种情况下,self 实际上是指individual 对象。

    我认为您总体上对 OOP 有一些困惑。继承根本不与特定对象实例交互。相反,这就像说:“我需要创建一个新对象,但让我将这个旧对象用作模板并添加到它”。

    您在这里尝试做的更接近于创建单例,这是一个具有在其他地方引用的单一状态的对象。并非一切都应该是一个对象。例如,处理程序通常是函数,依靠魔术execute 函数而不是仅仅执行函数是相当迂回的。

    【讨论】:

    • 非常感谢,很抱歉这么晚才回复,但是我现在对 OOP 有了更好的了解。非常感谢您的帮助!
    • @AaronOtillar 谢谢!乐意效劳。通常,当答案可以帮助您解决 SO 上的问题时,您可以通过单击问题旁边的复选标记来接受答案。你和我都会因此得到代表。
    【解决方案2】:

    正如 Slater Tyranus 所提到的,问题在于您正在创建 individual 的状态(wandereatsleep 类)子类。这是不正确的。将子类化视为说某事“是”另一件事。例如,对于类声明class Car(Vehicle):,您实质上是在说“CarVehicle”,这是一个真实的陈述。但是,您的代码暗示 wander 状态是个人,这是不正确的。另一个问题是,因为每个州都是独立的,所以每个州都有自己的 fsm。这不是必需的,个人也不应该首先拥有 fsm(你有吗?我没有)。

    我已经重构了您的代码,并简化了状态中的条件,更正了过程中的一些其他错误。看看这是否更符合您的预期:

    class FSM(object):
        def __init__(self, individual):
            self.states = {}
            self.c_state = None
            self.individual = individual
    
        def update(self):
            self.states[self.c_state].execute(self)
    
        def addState(self, name, handler):
            self.states[name] = handler
    
        def startState(self, state):
            self.c_state = state
    
        def changeState(self, newstate):
            print('The current state is:', self.c_state)
            self.states[self.c_state].exit()
            self.c_state = newstate
            self.states[self.c_state].enter()
    
    
    class Individual(object):
        def __init__(self, name, food, sleep):
            self.name = name
            self.food = food
            self.sleep = sleep
    
    
    class Wander():
        @staticmethod
        def enter():
            print('Entering Wander State')
    
        @staticmethod
        def execute(fsm):
            print('WANDERING')
            if fsm.individual.sleep <= 0:
                fsm.changeState('SLEEP')
            elif fsm.individual.food >= 25:
                fsm.changeState('WANDER')
                fsm.individual.sleep -= 5
                fsm.individual.food -= 5
            else:
                fsm.changeState('EAT')
    
        @staticmethod
        def exit():
            print('Leaving Wander State')
    
    
    class Eat():
        @staticmethod
        def enter():
            print('Entering Eating State')
    
        @staticmethod
        def execute(fsm):
            print('EATING')
            if fsm.individual.sleep <= 0:
                fsm.changeState('SLEEP')
            elif fsm.individual.food < 25:
                fsm.changeState('EAT')
                fsm.individual.food += 5
                fsm.individual.sleep -= 5
            else:
                fsm.changeState('WANDER')
    
        @staticmethod
        def exit():
            print('Exiting Eating State')
    
    
    class Sleep():
        @staticmethod
        def enter():
            print('Entering Sleeping State')
    
        @staticmethod
        def execute(fsm):
            print('SLEEPING')
            if fsm.individual.sleep < 50:
                fsm.changeState('SLEEP')
                fsm.individual.sleep += 5
            elif fsm.individual.food < 25:
                fsm.changeState('EAT')
            else:
                fsm.changeState('WANDER')
    
        @staticmethod
        def exit():
            print('Exiting Sleeping State')
    
    aaron = Individual('aaron', 10, 30)
    
    fsm = FSM(aaron)
    fsm.addState('WANDER', Wander)
    fsm.addState('EAT', Eat)
    fsm.addState('SLEEP', Sleep)
    fsm.startState('WANDER')
    print(fsm.c_state)
    fsm.update()
    

    【讨论】:

      猜你喜欢
      • 2015-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多