在Python中使用类实例作为参数调用方法

3
假设我有以下代码:
IDLE = 0
STARTED = 1
STOPPED = 2
ERRORED = 3
# additional states as needed

class StateMachine:
    def __init__(self)
        self.state = IDLE

    def start(self):
        self.state = STARTED
        # do something

    def stop(self):
        self.state = STOPPED
        # do something

    def reset(self):
        self.state = IDLE
        # do something

我们当前的界面允许客户通过说明所需的目标状态来更改实例的状态,此时我们运行某些验证检查,然后选择适当的方法。理想情况下,我希望保留一个字典映射所需的目标状态到正确的方法,以避免大量且无意义的if语句块。例如:
if target_state = STARTED:
    instance.start()
elif target_state = STOPPED:
    instance.stop()
...

但我不确定以下解决方案是否被认为是良好的实践(使用实例作为参数从类中调用方法感觉有点奇怪)。

state_mapping = {
    IDLE: StateMachine.reset,
    STARTED: StateMachine.start,
    ....
}

然后使用以下方式进行调用:

action = state_mapping[target_state]
action(instance)
....

有什么想法吗?

你尝试过使用 lambda 表达式吗? - Alon Alexander
我认为这个解决方案还不错。也许你应该考虑进行基准测试,以查看哪种方法更加高效。如果效率不是问题,那么可读性就很重要了。我个人认为 if elif elif else 的解决方案更易读 - 即使有点无聊。 - ChickenFeet
还有另一种方法,就是拥有一个总的状态机类,然后为每个状态创建一个子类,例如StateMachineStopped、StateMachineStarted。你可以使用self.__class__ = StateMachineStopped这样的方式在不同状态之间切换。请参见https://dev59.com/zWYr5IYBdhLWcg3w7uZx#24463654作为示例。 - JL Peyret
2个回答

2

并不是很疯狂。

然而,唯一需要记住的是action是一个未绑定的方法,在第一次调用该方法时可能并不明显;除非我亲自知道该字典是如何定义的。

我认为更易读的替代方案是从实例中调用该方法:

state_mapping = {
    IDLE: "reset",
    STARTED: "start",
    ....
}

action = state_mapping[target_state]
getattr(instance, action)()

当方法需要传递多个参数时,这将同样提高可读性。


谢谢您的反馈,我也考虑过这样做,但在这两种方法之间有些犹豫。现在您提到可读性,使用getattr就更有意义了。 - Andy

1

另一种选择。

由于您的类名为“StateMachine”,也许应该有一个执行状态更改的方法?

如果是这样,您可以在映射中使用绑定方法。

class StateMachine:
    ...

    def ChangeState(self, target):
        state_mapping = { IDLE: self.reset, STARTED: self.start, ... }
        state_mapping[target]()

您可能想处理无效的目标状态,或者只是让它引发一个KEY_ERROR异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接