这是执行函数的“Pythonic”方式吗?适用于元组值的Python switch语句?

7 投票
2 回答
10996 浏览
提问于 2025-04-17 04:48

我遇到了一种情况,有六种可能的情况可以对应四个不同的结果。我在想,是否可以用字典来代替冗长的if/else语句,这样就像在C#或PHP中使用“switch”语句一样,这样写会不会更符合Python的风格。

我的“switch”语句依赖于两个值,我用这两个值来构建一个元组,然后用这个元组作为字典的键,字典就充当我的“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]()

2 个回答

2

在Python编程中,根据字典或序列查找来调用函数是一种比较常见的做法。

考虑到你使用索引来查找,使用列表的列表也是可以的:

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

最符合Python风格的做法是既能清晰明了,又能满足其他操作需求。在你的例子中,查找的元组似乎没有什么特别的含义,这样就让操作的意图变得模糊了,像是用了一些神秘的常量。要确保你的业务逻辑在调用机制中不会被忽视。使用有意义的名称来命名这些常量会更有帮助。

17

考虑使用这个表达方式:

>>> 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>}

可读性很重要。

撰写回答