我想用Python编写一个函数,根据输入索引的值返回不同的固定值。

在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?


当前回答

我认为最好的方法是使用Python语言的习惯用法来保持代码的可测试性。如前面的回答所示,我使用字典来利用python结构和语言,并以不同的方法隔离“case”代码。下面是一个类,但您可以直接使用模块、全局变量和函数。该类具有可以隔离测试的方法。

根据您的需要,您也可以使用静态方法和属性。

class ChoiceManager:

    def __init__(self):
        self.__choice_table = \
        {
            "CHOICE1" : self.my_func1,
            "CHOICE2" : self.my_func2,
        }

    def my_func1(self, data):
        pass

    def my_func2(self, data):
        pass

    def process(self, case, data):
        return self.__choice_table[case](data)

ChoiceManager().process("CHOICE1", my_data)

也可以使用类作为“__choice_table”的键来利用此方法。通过这种方式,您可以避免信息滥用,并保持所有信息的清洁和可测试性。

假设您必须处理来自网络或MQ的大量消息或数据包。每个数据包都有自己的结构和管理代码(以通用方式)。

使用以上代码,可以执行以下操作:

class PacketManager:

    def __init__(self):
        self.__choice_table = \
        {
            ControlMessage : self.my_func1,
            DiagnosticMessage : self.my_func2,
        }

    def my_func1(self, data):
        # process the control message here
        pass

    def my_func2(self, data):
        # process the diagnostic message here
        pass

    def process(self, pkt):
        return self.__choice_table[pkt.__class__](pkt)

pkt = GetMyPacketFromNet()
PacketManager().process(pkt)


# isolated test or isolated usage example
def test_control_packet():
    p = ControlMessage()
    PacketManager().my_func1(p)

因此,复杂性不会在代码流中扩散,而是在代码结构中呈现。

其他回答

Python 3.10(2021)引入了match-case语句,该语句提供了Pythons“switch”的一流实现。例如:

def f(x):
    match x:
        case 'a':
            return 1
        case 'b':
            return 2
        case _:
            return 0   # 0 is the default case if x is not found

match-case语句比这个简单的示例强大得多。


以下原始答案写于2008年,当时还未提供匹配案例:

你可以用字典:

def f(x):
    return {
        'a': 1,
        'b': 2,
    }[x]

我在谷歌搜索上找不到简单的答案。但我还是想通了。这真的很简单。决定把它贴出来,也许可以防止别人的头上少刮几下。关键是简单的“in”和元组。下面是带有直通的switch语句行为,包括RANDOM直通。

l = ['Dog', 'Cat', 'Bird', 'Bigfoot',
     'Dragonfly', 'Snake', 'Bat', 'Loch Ness Monster']

for x in l:
    if x in ('Dog', 'Cat'):
        x += " has four legs"
    elif x in ('Bat', 'Bird', 'Dragonfly'):
        x += " has wings."
    elif x in ('Snake',):
        x += " has a forked tongue."
    else:
        x += " is a big mystery by default."
    print(x)

print()

for x in range(10):
    if x in (0, 1):
        x = "Values 0 and 1 caught here."
    elif x in (2,):
        x = "Value 2 caught here."
    elif x in (3, 7, 8):
        x = "Values 3, 7, 8 caught here."
    elif x in (4, 6):
        x = "Values 4 and 6 caught here"
    else:
        x = "Values 5 and 9 caught in default."
    print(x)

提供:

Dog has four legs
Cat has four legs
Bird has wings.
Bigfoot is a big mystery by default.
Dragonfly has wings.
Snake has a forked tongue.
Bat has wings.
Loch Ness Monster is a big mystery by default.

Values 0 and 1 caught here.
Values 0 and 1 caught here.
Value 2 caught here.
Values 3, 7, 8 caught here.
Values 4 and 6 caught here
Values 5 and 9 caught in default.
Values 4 and 6 caught here
Values 3, 7, 8 caught here.
Values 3, 7, 8 caught here.
Values 5 and 9 caught in default.

我倾向于使用字典的解决方案是:

def decision_time( key, *args, **kwargs):
    def action1()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action2()
        """This function is a closure - and has access to all the arguments"""
        pass
    def action3()
        """This function is a closure - and has access to all the arguments"""
        pass

   return {1:action1, 2:action2, 3:action3}.get(key,default)()

这样做的优点是它不需要每次都对函数求值,您只需确保外部函数获得内部函数所需的所有信息。

我认为最好的方法是使用Python语言的习惯用法来保持代码的可测试性。如前面的回答所示,我使用字典来利用python结构和语言,并以不同的方法隔离“case”代码。下面是一个类,但您可以直接使用模块、全局变量和函数。该类具有可以隔离测试的方法。

根据您的需要,您也可以使用静态方法和属性。

class ChoiceManager:

    def __init__(self):
        self.__choice_table = \
        {
            "CHOICE1" : self.my_func1,
            "CHOICE2" : self.my_func2,
        }

    def my_func1(self, data):
        pass

    def my_func2(self, data):
        pass

    def process(self, case, data):
        return self.__choice_table[case](data)

ChoiceManager().process("CHOICE1", my_data)

也可以使用类作为“__choice_table”的键来利用此方法。通过这种方式,您可以避免信息滥用,并保持所有信息的清洁和可测试性。

假设您必须处理来自网络或MQ的大量消息或数据包。每个数据包都有自己的结构和管理代码(以通用方式)。

使用以上代码,可以执行以下操作:

class PacketManager:

    def __init__(self):
        self.__choice_table = \
        {
            ControlMessage : self.my_func1,
            DiagnosticMessage : self.my_func2,
        }

    def my_func1(self, data):
        # process the control message here
        pass

    def my_func2(self, data):
        # process the diagnostic message here
        pass

    def process(self, pkt):
        return self.__choice_table[pkt.__class__](pkt)

pkt = GetMyPacketFromNet()
PacketManager().process(pkt)


# isolated test or isolated usage example
def test_control_packet():
    p = ControlMessage()
    PacketManager().my_func1(p)

因此,复杂性不会在代码流中扩散,而是在代码结构中呈现。

我发现Python文档中的以下答案非常有用:

你可以通过一系列if…elif。。。埃利夫。。。其他的关于switch语句语法已经有了一些建议,但对于是否以及如何进行范围测试还没有达成共识。有关完整详细信息和当前状态,请参见PEP 275。

对于需要从大量可能性中进行选择的情况,可以创建一个字典,将大小写值映射到要调用的函数。例如:

def function_1(...):
    ...

functions = {'a': function_1,
             'b': function_2,
             'c': self.method_1, ...}

func = functions[value]
func()

对于在对象上调用方法,可以通过使用内置的getattr()来检索具有特定名称的方法来进一步简化:

def visit_a(self, ...):
    ...
...

def dispatch(self, value):
    method_name = 'visit_' + str(value)
    method = getattr(self, method_name)
    method()

建议为方法名称使用前缀,例如本例中的visit_。如果没有这样的前缀,如果值来自不受信任的源,攻击者将能够调用对象上的任何方法。