太多的if语句

13

我有一些话题要讨论。我有一个包含24个if/elif的代码片段。Operation是我自己创建的一个类,它代表了类似于Enum的功能。
这是代码片段:

if operation == Operation.START:
    strategy = strategy_objects.StartObject()
elif operation == Operation.STOP:
    strategy = strategy_objects.StopObject()
elif operation == Operation.STATUS:
    strategy = strategy_objects.StatusObject()
(...)

从可读性角度来看,我有一些担忧。将其改为24个类并使用多态是否更好?我并不确定这会使我的代码易于维护...... 从一方面来说,这些if非常清晰,应该很容易理解,但另一方面,if太多了。

我的问题比较普遍,但是我正在用Python编写代码,因此无法使用像switch这样的结构。

你怎么看?


更新:

一个重要的事情是,StartObject()StopObject()StatusObject()是构造函数,并且我想将一个对象分配给strategy引用。


case语句,还是使用方法引用的数组以动态调用,例如python中等价于 array_of_methods[operation]()的方式... - Marc B
3
Marc,Python没有switch/case语句。您可以查看以下链接了解更多信息:https://www.python.org/dev/peps/pep-3103/ - Stiffo
7个回答

19

你可以使用字典。字典存储的是引用(references),这意味着函数完全可以使用,就像这样:

operationFuncs = {
    Operation.START: strategy_objects.StartObject
    Operation.STOP: strategy_objects.StopObject
    Operation.STATUS: strategy_objects.StatusObject
    (...)                  
}

为了保险起见,最好设置一个默认操作,在运行时使用try except语句并处理异常(即等效于您的else子句)。

try:
    strategy = operationFuncs[operation]()
except KeyError:
    strategy = strategy_objects.DefaultObject()

或者使用字典的get方法,如果提供的键不存在,它允许您指定一个默认值。

strategy = operationFuncs.get(operation(), DefaultObject())
请注意,在将其存储在字典中时,不要包括括号,只有在调用字典时才使用它们。此外,这需要Operation.START是可哈希的,但应该符合条件,因为您将其描述为类似于ENUM的类。

4

在Python中,相当于switch语句的是使用字典。实际上,您可以将键存储为案例,并且值就是针对特定情况所调用的内容。由于在Python中函数本身就是对象,因此您可以将其作为字典的值存储:

operation_dispatcher = {
    Operation.START: strategy_objects.StartObject,
    Operation.STOP: strategy_objects.StopObject,
}

接下来可以这样使用:

try:
    strategy = operation_dispatcher[operation] #fetch the strategy
except KeyError:
    strategy = default #this deals with the else-case (if you have one)
strategy() #call if needed

更简洁地说:
strategy = operation_dispatcher.get(operation, default)
strategy() #call if needed

使用字典来处理操作可能比混乱的if-else语句更具扩展性。请注意,如果您没有else情况需要处理,您可以直接使用operation_dispatcher[operation]


4
你可以尝试像这样的方法。
例如:
def chooseStrategy(op):
    return {
        Operation.START: strategy_objects.StartObject
        Operation.STOP: strategy_objects.StopObject
    }.get(op, strategy_objects.DefaultValue)

这样调用:
strategy = chooseStrategy(operation)()

这种方法的好处是提供了一个默认值(类似于最终的else语句)。当然,如果你只需要在代码中的一个地方使用这个决策逻辑,你可以直接使用strategy = dictionary.get(op, default),而不必使用这个函数。


你错过了调用。strategy = chooseStrategy(operation)() - Brobin
谢谢!我错过了他正在调用所选择的策略。我已经编辑了我的帖子。 - weirdev

2

1
您可以使用getattr进行一些内省操作:
 strategy = getattr(strategy_objects, "%sObject" % operation.capitalize())()

假设操作为“STATUS”,则应将其大写为“Status”,然后添加到“Object”之前,得到“StatusObject”。然后在“strategy_objects”上调用StatusObject方法,如果该属性不存在或者不可调用,则会发生灾难性失败。 :) (即添加错误处理。)使用字典解决方案可能更加灵活。

使用 hasattr(...) 检查类是否存在相当于在字典解决方案中使用 KeyError。 - Julian Go

0
如果Operation.START等是可哈希的,您可以使用字典,将键作为条件,将值作为要调用的函数,例如 -
d = {Operation.START: strategy_objects.StartObject , 
     Operation.STOP: strategy_objects.StopObject, 
     Operation.STATUS: strategy_objects.StatusObject}

然后你可以进行字典查找并调用函数,例如 -

d[operation]()

-1
这是一个使用字典实现的变形switch/case示例:
例如:
# define the function blocks
def start():
    strategy = strategy_objects.StartObject()

def stop():
    strategy = strategy_objects.StopObject()

def status():
    strategy = strategy_objects.StatusObject()

# map the inputs to the function blocks
options = {"start" : start,
           "stop" : stop,
           "status" : status,

}

然后调用等效的 switch 块:

options["string"]()

1
但是strategy将局限于这些小函数的每个本地作用域中,并且在执行options["string"]()的作用域中不可见。 - Kevin

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