Является ли это «питоновским» методом выполнения функций в качестве оператора переключения python для значений кортежей?

#python

#python

Вопрос:

У меня ситуация, когда у меня есть шесть возможных ситуаций, которые могут относиться к четырем различным результатам. Вместо использования расширенного оператора if / else мне было интересно, было бы более питоновским использовать словарь для вызова функций, которые я бы вызывал внутри if / else в качестве замены оператора «switch», как это можно использовать в C # или php.

Мой оператор switch зависит от двух значений, которые я использую для построения кортежа, который я, в свою очередь, буду использовать как ключ к словарю, который будет функционировать как мой «переключатель». Я буду получать значения для кортежа из двух других функций (вызовов базы данных), поэтому у меня есть пример функций one() и zero() .

Это шаблон кода, который я собираюсь использовать, на который я наткнулся, играя в оболочке python:

 def one():
    #Simulated database value
    return 1

def zero():
    return 0

def run():
    #Shows the correct function ran
    print "RUN"
    return 1

def walk():
    print "WALK"
    return 1

def main():
    switch_dictionary = {}

    #These are the values that I will want to use to decide
    #which functions to use
    switch_dictionary[(0,0)] = run
    switch_dictionary[(1,1)] = walk

    #These are the tuples that I will build from the database
    zero_tuple = (zero(), zero())
    one_tuple = (one(), one())

    #These actually run the functions. In practice I will simply 
    #have the one tuple which is dependent on the database information
    #to run the function that I defined before
    switch_dictionary[zero_tuple]()
    switch_dictionary[one_tuple]()
 

У меня нет фактического написанного кода, или я бы опубликовал его здесь, так как я хотел бы знать, считается ли этот метод лучшей практикой python. Я все еще изучаю python в университете, и если это метод, который является плохой привычкой, то я хотел бы избавиться от него сейчас, прежде чем я выйду в реальный мир.

Обратите внимание, результат выполнения приведенного выше кода соответствует ожидаемому, просто «RUN» и «WALK».

Редактировать

Для тех из вас, кому интересно, вот как получился соответствующий код. Он используется в приложении Google App Engine. Вы должны обнаружить, что код значительно аккуратнее, чем мой примерный пример. Он работает намного лучше, чем мое предыдущее запутанное дерево if / else.

 def GetAssignedAgent(self):
    tPaypal = PaypalOrder() #Parent class for this function
    tAgents = []
    Switch = {}

    #These are the different methods for the actions to take
    Switch[(0,0)] = tPaypal.AssignNoAgent
    Switch[(0,1)] = tPaypal.UseBackupAgents
    Switch[(0,2)] = tPaypal.UseBackupAgents
    Switch[(1,0)] = tPaypal.UseFullAgents
    Switch[(1,1)] = tPaypal.UseFullAndBackupAgents
    Switch[(1,2)] = tPaypal.UseFullAndBackupAgents
    Switch[(2,0)] = tPaypal.UseFullAgents
    Switch[(2,1)] = tPaypal.UseFullAgents
    Switch[(2,2)] = tPaypal.UseFullAgents

    #I'm only interested in the number up to 2, which is why
    #I can consider the Switch dictionary to be all options available.
    #The "state" is the current status of the customer agent system
    tCurrentState = (tPaypal.GetNumberofAvailableAgents(), 
                     tPaypal.GetNumberofBackupAgents())

    tAgents = Switch[tCurrentState]()
 

Комментарии:

1. Намного лучше! Очень читаемый и «питонический»

2. Обратите внимание, что это Switch[(x,y)] может быть записано более четко как Switch[x, y]

Ответ №1:

Вместо этого рассмотрим эту идиому:

 >>> def run():
...   print 'run'
... 
>>> def walk():
...   print 'walk'
... 
>>> def talk():
...    print 'talk'
>>> switch={'run':run,'walk':walk,'talk':talk}
>>> switch['run']()
run
 

Я думаю, что это немного более читабельно, чем направление, в котором вы движетесь.

Редактировать

И это тоже работает:

 >>> switch={0:run,1:walk} 
>>> switch[0]()
run
>>> switch[max(0,1)]()
walk
 

Вы даже можете использовать эту идиому для switch / default структуры типов:

 >>> default_value=1
>>> try:
...    switch[49]()
... except KeyError:
...    switch[default_value]()
 

Или (менее читаемый, более краткий):

 >>> switch[switch.get(49,default_value)]()
walk
 

редактировать 2

Та же идиома, расширенная до вашего комментария:

 >>> def get_t1():
...    return 0
... 
>>> def get_t2():
...    return 1
... 
>>> switch={(get_t1(),get_t2()):run}
>>> switch
{(0, 1): <function run at 0x100492d70>}
 

Читаемость имеет значение

Комментарии:

1. Проблема в том, что я буду динамически создавать кортеж на основе вызовов базы данных, поскольку я уже работаю над возвратом 0, 1 или 2 на основе результатов базы данных в функции. Поэтому использование кортежей против отдельных строк действительно очень необходимо, поскольку мне нужно использовать два (возможно, больше в будущем) значения. Мне действительно нравится идея использования «switch» в качестве имени словаря.

2. Возможно, вы захотите рассмотреть возможность создания простого класса. Общая «питоническая» вещь заключается в том, чтобы помнить, что удобочитаемость имеет большое значение. Что-то вроде switch_dictionary[(0,0)] = run и zero_tuple = (zero(), zero()) другим (или вам самим через несколько месяцев) трудно расшифровать. Python имеет тенденцию быть очень читаемым. Выбранные вами идиомы имеют значение. Вы можете написать этот же код более читаемым способом.

3. Моя конкретная проблема похожа на систему обслуживания клиентов, где она назначает запросы клиентов двум разным типам агентов обслуживания клиентов (каждая половина кортежа в моем описании), но она меняет свое поведение в зависимости от количества агентов каждого типа в Сети. Кортеж в будет чем-то вроде (GetTier1(), GetTier2()) , так что, возможно, кортеж в этом случае наиболее удобочитаем? Я не привык, чтобы другие читали мой код на python, поэтому мне искренне интересно.

4. Я добавил то, что решил использовать, в первый пост. Вы должны найти реализацию значительно лучше и гораздо более читаемой, чем мой первый шаблон.

5. switch[switch.get(49,default_value)]() должно быть switch.get(49, switch.get(default_value))()

Ответ №2:

Достаточно распространенной практикой python является отправка функций на основе поиска по словарю или последовательности.

Учитывая, что вы используете индексы для поиска, список списков также будет работать:

 switch_list = [[run, None], [None, walk]]
  ...
switch_list[zero_tuple]()
 

То, что считается наиболее Питоническим, — это то, что обеспечивает максимальную четкость при одновременном удовлетворении других эксплуатационных требований. В вашем примере кортеж подстановки, по-видимому, не имеет внутреннего значения, поэтому оперативное намерение теряется из-за магической константы. Постарайтесь сделать так, чтобы бизнес-логика не терялась в вашем механизме отправки. Вероятно, поможет использование значимых имен для констант.