Слишком много операторов if

у меня есть тема для обсуждения. У меня есть фрагмент кода с 24 ifs/elifs. 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 класса и использовать полиморфизм? Я не уверен, что это сделает мой код доступным для обслуживания... С одной стороны те ifs довольно ясны и это не должно быть трудно следовать, с другой стороны есть слишком много ifs.

мой вопрос довольно общий, однако я пишу код на Python, поэтому я не могу использовать такие конструкции, как switch.

что вы думаете?


обновление:

одна важная вещь это StartObject(), StopObject() и StatusObject() являются конструкторами, и я хотел назначить объект strategy ссылка.

6 ответов


вы могли бы использовать словарь. Словари хранят ссылки, что означает, что функции вполне жизнеспособны для использования, например:

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 быть hashable, но это должно быть так, как вы это описали как класс, похожими на перечисление.


эквивалент 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 заявления. Обратите внимание, что если у вас нет другого случая, вы можете просто использовать словарь непосредственно с operation_dispatcher[operation].


вы можете попробовать что-то вроде этой.

например:

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, по умолчанию) без функции.


вы можете использовать некоторую интроспекцию с getattr:

 strategy = getattr(strategy_objects, "%sObject" % operation.capitalize())()

предположим, что операция является "STATUS", она будет капитализирована как" Status", а затем добавлена к" Object", давая"StatusObject". The StatusObject метод будет вызван на strategy_objects, сбой катастрофически, если этот атрибут не существует или если он не вызывается. :) (Т. е. добавить обработку ошибок.)

словарное решение, вероятно,более гибкое.


Если Operation.START и т. д. хэшируются, вы можете использовать словарь с ключами в качестве условия и значения в качестве функций для вызова, например -

d = {Operation.START: strategy_objects.StartObject , 
     Operation.STOP: strategy_objects.StopObject, 
     Operation.STATUS: strategy_objects.StatusObject}

и затем вы можете сделать этот поиск словаря и вызвать функцию, например -

d[operation]()

вот ублюдочный переключатель / случай, сделанный с помощью словарей:

например:

# 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,

}

затем вызывается эквивалентный блок коммутатора:

options["string"]()