在Python中替换Switch语句?

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

在其他语言中,我会使用switchcase语句,但Python似乎没有switch语句。在这种情况下,推荐的Python解决方案是什么?

2183234 次浏览

下面的原始答案写于2008年。从那时起,Python 3.10(2021)引入了#0-#1语句,它为Python提供了“开关”的一流实现。例如:

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

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


你可以使用字典:

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

我一直喜欢这样做

result = {'a': lambda x: x * 5,'b': lambda x: x + 7,'c': lambda x: x - 2}[value](x)

从这里

如果你真的只是返回一个预定的、固定的值,你可以创建一个字典,其中包含所有可能的输入索引作为键,以及它们的相应值。此外,你可能并不真的想要一个函数来做这件事——除非你以某种方式计算返回值。

哦,如果你想做一些类似开关的事情,请参阅这里

除了字典方法(我非常喜欢,顺便说一句),您还可以使用if-elif-else来获得switch/case/default功能:

if x == 'a':# Do the thingelif x == 'b':# Do the other thingif x in 'bc':# Fall-through by not using elif, but now the default case includes case 'a'!elif x in 'xyz':# Do yet another thingelse:# Do the default

当然,这与Switch/case不同——你不能像省略break语句那样轻松地进行失败测试,但你可以进行更复杂的测试。它的格式比一系列嵌套的if更好,即使在功能上它更接近。

我从Twisted Python代码中学到了一个模式。

class SMTP:def lookupMethod(self, command):return getattr(self, 'do_' + command.upper(), None)def do_HELO(self, rest):return 'Howdy ' + restdef do_QUIT(self, rest):return 'Bye'
SMTP().lookupMethod('HELO')('foo.bar.com') # => 'Howdy foo.bar.com'SMTP().lookupMethod('QUIT')('') # => 'Bye'

您可以在任何需要对令牌进行调度并执行扩展代码的时候使用它。在状态机中,您将拥有state_个方法,并在self.state上进行调度。通过继承基类并定义自己的do_方法,可以干净地扩展此开关。通常情况下,您甚至不会在基类中拥有do_方法。

编辑:这是如何使用的

在SMTP的情况下,您将从电线中收到HELO。相关代码(来自twisted/mail/smtp.py,为我们的案例修改)如下所示

class SMTP:# ...
def do_UNKNOWN(self, rest):raise NotImplementedError, 'received unknown command'
def state_COMMAND(self, line):line = line.strip()parts = line.split(None, 1)if parts:method = self.lookupMethod(parts[0]) or self.do_UNKNOWNif len(parts) == 2:return method(parts[1])else:return method('')else:raise SyntaxError, 'bad syntax'
SMTP().state_COMMAND('   HELO   foo.bar.com  ') # => Howdy foo.bar.com

您将收到' HELO foo.bar.com '(或者您可能会收到'QUIT''RCPT TO: foo')。这被标记为parts作为['HELO', 'foo.bar.com']。实际的方法查找名称取自parts[0]

(原始方法也称为state_COMMAND,因为它使用相同的模式来实现状态机,即getattr(self, 'state_' + self.mode)

扩展“将字典作为开关”的想法。如果您想为您的开关使用默认值:

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

如果你喜欢默认值,你可以使用字典#0函数:

def f(x):return {'a': 1,'b': 2}.get(x, 9)    # 9 will be returned default if x is not found

我使用的解决方案:

这里发布的2个解决方案的组合,相对容易阅读并支持默认值。

result = {'a': lambda x: x * 5,'b': lambda x: x + 7,'c': lambda x: x - 2}.get(whatToUse, lambda x: x - 22)(value)

在哪里

.get('c', lambda x: x - 22)(23)

在字典中查找"lambda x: x - 2"并将其与x=23一起使用

.get('xxx', lambda x: x - 22)(44)

在字典中找不到它,并将默认的"lambda x: x - 22"x=44一起使用。

假设你不想只返回一个值,而是想使用改变对象上某些东西的方法。使用这里所述的方法将是:

result = {'a': obj.increment(x),'b': obj.decrement(x)}.get(value, obj.default(x))

这里Python评估字典中的所有方法。

所以即使你的值是'a',对象也会递增递减x。

解决方案:

func, args = {'a' : (obj.increment, (x,)),'b' : (obj.decrement, (x,)),}.get(value, (obj.default, (x,)))
result = func(*args)

所以你得到一个包含函数及其参数的列表。这样,只有函数指针和参数列表得到返回,没有评估。“结果”然后评估返回的函数调用。

如果你正在搜索额外语句,作为“开关”,我构建了一个扩展Python的Python模块。它被称为ESPY作为“Python的增强结构”,它可用于Python 2. x和Python 3. x。

例如,在这种情况下,可以通过以下代码执行Switch语句:

macro switch(arg1):while True:cont=Falseval=%arg1%socket case(arg2):if val==%arg2% or cont:cont=Truesocketsocket else:socketbreak

它可以像这样使用:

a=3switch(a):case(0):print("Zero")case(1):print("Smaller than 2"):breakelse:print ("greater than 1")

所以espy在Python中将其翻译为:

a=3while True:cont=Falseif a==0 or cont:cont=Trueprint ("Zero")if a==1 or cont:cont=Trueprint ("Smaller than 2")breakprint ("greater than 1")break
class switch(object):value = Nonedef __new__(class_, value):class_.value = valuereturn True
def case(*args):return any((arg == switch.value for arg in args))

用法:

while switch(n):if case(0):print "You typed zero."breakif case(1, 4, 9):print "n is a perfect square."breakif case(2):print "n is an even number."if case(2, 3, 5, 7):print "n is a prime number."breakif case(6, 8):print "n is an even number."breakprint "Only single-digit numbers are allowed."break

测试:

n = 2#Result:#n is an even number.#n is a prime number.n = 11#Result:#Only single-digit numbers are allowed.

我最喜欢的是一个非常好的食谱。这是我见过的最接近实际的Switch case语句的语句,尤其是在功能方面。

class switch(object):def __init__(self, value):self.value = valueself.fall = False
def __iter__(self):"""Return the match method once, then stop"""yield self.matchraise StopIteration    
def match(self, *args):"""Indicate whether or not to enter a case suite"""if self.fall or not args:return Trueelif self.value in args: # changed for v1.5, see belowself.fall = Truereturn Trueelse:return False

这里有一个例子:

# The following example is pretty much the exact use-case of a dictionary,# but is included for its simplicity. Note that you can include statements# in each suite.v = 'ten'for case in switch(v):if case('one'):print 1breakif case('two'):print 2breakif case('ten'):print 10breakif case('eleven'):print 11breakif case(): # default, could also just omit condition or 'if True'print "something else!"# No need to break here, it'll stop anyway
# break is used here to look as much like the real thing as possible, but# elif is generally just as good and more concise.
# Empty suites are considered syntax errors, so intentional fall-throughs# should contain 'pass'c = 'z'for case in switch(c):if case('a'): pass # only necessary if the rest of the suite is emptyif case('b'): pass# ...if case('y'): passif case('z'):print "c is lowercase!"breakif case('A'): pass# ...if case('Z'):print "c is uppercase!"breakif case(): # defaultprint "I dunno what c was!"
# As suggested by Pierre Quentel, you can even expand upon the# functionality of the classic 'case' statement by matching multiple# cases in a single shot. This greatly benefits operations such as the# uppercase/lowercase example above:import stringc = 'A'for case in switch(c):if case(*string.lowercase): # note the * for unpacking as argumentsprint "c is lowercase!"breakif case(*string.uppercase):print "c is uppercase!"breakif case('!', '?', '.'): # normal argument passing style also appliesprint "c is a sentence terminator!"breakif case(): # defaultprint "I dunno what c was!"

一些评论表明,使用with foo as case而不是for case in foo的上下文管理器解决方案可能更干净,对于大型Switch语句,线性而不是二次行为可能是一个很好的接触。这个使用for循环的答案的部分价值是具有中断和失败的能力,如果我们愿意稍微玩弄我们选择的关键字,我们也可以在上下文管理器中获得它:

class Switch:def __init__(self, value):self.value = valueself._entered = Falseself._broken = Falseself._prev = None
def __enter__(self):return self
def __exit__(self, type, value, traceback):return False # Allows a traceback to occur
def __call__(self, *values):if self._broken:return False        
if not self._entered:if values and self.value not in values:return Falseself._entered, self._prev = True, valuesreturn True        
if self._prev is None:self._prev = valuesreturn True        
if self._prev != values:self._broken = Truereturn False        
if self._prev == values:self._prev = Nonereturn False    
@propertydef default(self):return self()

这里有一个例子:

# Prints 'bar' then 'baz'.with Switch(2) as case:while case(0):print('foo')while case(1, 2, 3):print('bar')while case(4, 5):print('baz')breakwhile case.default:print('default')break

如果您有一个复杂的case块,您可以考虑使用函数字典查找表…

如果您之前没有这样做过,最好进入调试器并准确查看字典如何查找每个函数。

注意:没有在case/case查找中使用“()”,否则它将在创建字典/case块时调用您的每个函数。请记住这一点,因为您只想使用哈希样式查找调用每个函数一次。

def first_case():print "first"
def second_case():print "second"
def third_case():print "third"
mycase = {'first': first_case, #do not use ()'second': second_case, #do not use ()'third': third_case #do not use ()}myfunc = mycase['first']myfunc()
def f(x):return 1 if x == 'a' else\2 if x in 'bcd' else\0 #default

简短易读,具有默认值,并支持条件和返回值的表达式。

但是,它的效率低于使用字典的解决方案。例如,Python必须在返回默认值之前扫描所有条件。

只是将一些键映射到一些代码并不是一个真正的问题,因为大多数人已经使用了字典。真正的诀窍是试图模拟整个下拉和中断的事情。我想我从来没有写过使用那个“特性”的case语句。这是一个下拉。

def case(list): reduce(lambda b, f: (b | f[0], {False:(lambda:None),True:f[1]}[b | f[0]]())[0], list, False)
case([(False, lambda:print(5)),(True, lambda:print(4))])

我真的把它想象成一个单一的声明。我希望你能原谅愚蠢的格式。

reduce(initializer=False,function=(lambda b, f:( b | f[0], { False: (lambda:None), True : f[1]}[b | f[0]]())[0]),iterable=[(False, lambda:print(5)),(True, lambda:print(4))])

我希望这是有效的Python代码。它应该给你下拉通过。当然,布尔检查可以是表达式,如果你想延迟计算它们,你可以将它们全部包装在一个lambda中。在执行列表中的一些项目后,让它接受也不难。只需制作元组(bool, bool, Func),其中第二个bool指示是否突破或下拉通过。

也使用列表来存储案例,并通过选择调用相应的函数-

cases = ['zero()', 'one()', 'two()', 'three()']
def zero():print "method for 0 called..."def one():print "method for 1 called..."def two():print "method for 2 called..."def three():print "method for 3 called..."
i = int(raw_input("Enter choice between 0-3 "))
if(i<=len(cases)):exec(cases[i])else:print "wrong choice"

也解释了螺丝桌

定义:

def switch1(value, options):if value in options:options[value]()

允许您使用相当简单的语法,将案例捆绑到地图中:

def sample1(x):local = 'betty'switch1(x, {'a': lambda: print("hello"),'b': lambda: (print("goodbye," + local),print("!")),})

我一直试图以一种让我摆脱“lambda:”的方式重新定义Switch,但放弃了。调整定义:

def switch(value, *maps):options = {}for m in maps:options.update(m)if value in options:options[value]()elif None in options:options[None]()

允许我将多个案例映射到相同的代码,并提供默认选项:

def sample(x):switch(x, {_: lambda: print("other")for _ in 'cdef'}, {'a': lambda: print("hello"),'b': lambda: (print("goodbye,"),print("!")),None: lambda: print("I dunno")})

每个复制的case都必须在自己的字典中;Switch()在查找值之前合并字典。它仍然比我想象的要丑陋,但它具有对表达式使用散列查找的基本效率,而不是循环遍历所有键。

我在谷歌搜索中没有找到我要找的简单答案。但我还是想通了。它真的很简单。决定发布它,也许可以防止在别人头上少抓几下。关键是“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 legsCat has four legsBird 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 hereValues 5 and 9 caught in default.Values 4 and 6 caught hereValues 3, 7, 8 caught here.Values 3, 7, 8 caught here.Values 5 and 9 caught in default.

喜欢Mark Bies的回答

由于x变量必须使用两次,我将lambda函数修改为无参数。

我必须和results[value](value)一起跑

In [2]: result = {...:   'a': lambda x: 'A',...:   'b': lambda x: 'B',...:   'c': lambda x: 'C'...: }...: result['a']('a')...:Out[2]: 'A'
In [3]: result = {...:   'a': lambda : 'A',...:   'b': lambda : 'B',...:   'c': lambda : 'C',...:   None: lambda : 'Nothing else matters'
...: }...: result['a']()...:Out[3]: 'A'

编辑:我注意到我可以将None类型与字典一起使用。所以这将模拟switch ; case else

我发现一个常见的开关结构:

switch ...parameter...case p1: v1; break;case p2: v2; break;default: v3;

可以用Python表示如下:

(lambda x: v1 if p1(x) else v2 if p2(x) else v3)

或以更清晰的方式格式化:

(lambda x:v1 if p1(x) elsev2 if p2(x) elsev3)

Python版本不是语句,而是表达式,计算结果为一个值。

class Switch:def __init__(self, value):self.value = value
def __enter__(self):return self
def __exit__(self, type, value, traceback):return False # Allows a traceback to occur
def __call__(self, *values):return self.value in values

from datetime import datetime
with Switch(datetime.today().weekday()) as case:if case(0):# Basic usage of switchprint("I hate mondays so much.")# Note there is no break needed hereelif case(1,2):# This switch also supports multiple conditions (in one line)print("When is the weekend going to be here?")elif case(3,4):print("The weekend is near.")else:# Default would occur hereprint("Let's go have fun!") # Didn't use case for example purposes

Python>=3.10

哇,Python 3.10+现在有一个#0/#1语法,就像switch/case甚至更多!

PEP 634--结构模式匹配

match/case的选定功能

1-匹配值:

匹配值类似于另一种语言中的简单switch/case

match something:case 1 | 2 | 3:# Match 1-3.case _:# Anything else.## Match will throw an error if this is omitted# and it doesn't match any of the other patterns.

2-匹配结构模式:

match something:case str() | bytes():# Match a string like object.case [str(), int()]:# Match a `str` and an `int` sequence# (`list` or a `tuple` but not a `set` or an iterator).case [_, _]:# Match a sequence of 2 variables.# To prevent a common mistake, sequence patterns don’t match strings.case {"bandwidth": 100, "latency": 300}:# Match this dict. Extra keys are ignored.

3-捕获变量

解析一个对象;将其保存为变量:

match something:case [name, count]# Match a sequence of any two objects and parse them into the two variables.case [x, y, *rest]:# Match a sequence of two or more objects,# binding object #3 and on into the rest variable.case bytes() | str() as text:# Match any string like object and save it to the text variable.

捕获变量在解析可能以多种不同模式之一出现的数据(例如JSON或超文本标记语言)时非常有用。

捕获变量是一项功能。但这也意味着您只需要使用虚线常量(例如:COLOR.RED)。否则,常量将被视为捕获变量并被覆盖。

更多样本使用

match something:case 0 | 1 | 2:# Matches 0, 1 or 2 (value).print("Small number")case [] | [_]:# Matches an empty or single value sequence (structure).# Matches lists and tuples but not sets.print("A short sequence")case str() | bytes():# Something of `str` or `bytes` type (data type).print("Something string-like")case _:# Anything not matched by the above.print("Something else")

Python<=3.9

我最喜欢的Switch/case的Python配方是:

choices = {'a': 1, 'b': 2}result = choices.get(key, 'default')

简短而简单的简单场景。

比较11+行C代码:

// C Language version of a simple 'switch/case'.switch( key ){case 'a' :result = 1;break;case 'b' :result = 2;break;default :result = -1;}

您甚至可以使用元组分配多个变量:

choices = {'a': (1, 2, 3), 'b': (4, 5, 6)}(result1, result2, result3) = choices.get(key, ('default1', 'default2', 'default3'))
# simple case alternative
some_value = 5.0
# this while loop block simulates a case block
# casewhile True:
# case 1if some_value > 5:print ('Greater than five')break
# case 2if some_value == 5:print ('Equal to five')break
# else case 3print ( 'Must be less than 5')break

我认为最好的方法是使用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 herepass
def my_func2(self, data):# process the diagnostic message herepass
def process(self, pkt):return self.__choice_table[pkt.__class__](pkt)
pkt = GetMyPacketFromNet()PacketManager().process(pkt)

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

所以复杂性不会在代码流中传播,而是在代码结构中呈现

如果您不担心在case套件中丢失语法高亮显示,您可以执行以下操作:

exec {1: """print ('one')""",2: """print ('two')""",3: """print ('three')""",}.get(value, """print ('None')""")

其中value是值。在C中,这将是:

switch (value) {case 1:printf("one");break;case 2:printf("two");break;case 3:printf("three");break;default:printf("None");break;}

我们也可以创建一个辅助函数来做到这一点:

def switch(value, cases, default):exec cases.get(value, default)

所以我们可以像这样使用它,例如1、2和3:

switch(value, {1: """print ('one')""",2: """print ('two')""",3: """print ('three')""",}, """print ('None')""")

扩展Greg Hewgill的回答-我们可以使用装饰器封装字典解决方案:

def case(callable):"""switch-case decorator"""class case_class(object):def __init__(self, *args, **kwargs):self.args = argsself.kwargs = kwargs
def do_call(self):return callable(*self.args, **self.kwargs)
return case_class
def switch(key, cases, default=None):"""switch-statement"""ret = Nonetry:ret = case[key].do_call()except KeyError:if default:ret = default.do_call()finally:return ret

这可以与@case-装饰器一起使用

@casedef case_1(arg1):print 'case_1: ', arg1
@casedef case_2(arg1, arg2):print 'case_2'return arg1, arg2
@casedef default_case(arg1, arg2, arg3):print 'default_case: ', arg1, arg2, arg3
ret = switch(somearg, {1: case_1('somestring'),2: case_2(13, 42)}, default_case(123, 'astring', 3.14))
print ret

好消息是这已经在NeoPySwitch模块中完成了。只需使用pip安装:

pip install NeoPySwitch

读完接受的答案后,我很困惑,但这一切都清楚了:

def numbers_to_strings(argument):switcher = {0: "zero",1: "one",2: "two",}return switcher.get(argument, "nothing")

这段代码类似于:

function(argument){switch(argument) {case 0:return "zero";case 1:return "one";case 2:return "two";default:return "nothing";}}

检查来源以获取有关字典映射到函数的更多信息。

Switch语句只是if/elif/else的语法糖。任何控制语句所做的都是根据正在实现的特定条件委托作业-决策路径。为了将其包装到模块中并能够根据其唯一ID调用作业,可以使用继承和Python中的任何方法都是虚拟的这一事实,来提供派生类特定的作业实现,作为特定的“case”处理程序:

#!/usr/bin/python
import sys
class Case(object):"""Base class which specifies the interface for the "case" handler.The all required arbitrary arguments inside "execute" method will beprovided through the derived classspecific constructor
@note in Python, all class methods are virtual"""def __init__(self, id):self.id = id
def pair(self):"""Pairs the given id of the "case" withthe instance on which "execute" will be called"""return (self.id, self)
def execute(self): # Base class virtual method that needs to be overriddenpass
class Case1(Case):def __init__(self, id, msg):self.id = idself.msg = msgdef execute(self): # Override the base class methodprint("<Case1> id={}, message: \"{}\"".format(str(self.id), self.msg))
class Case2(Case):def __init__(self, id, n):self.id = idself.n = ndef execute(self): # Override the base class methodprint("<Case2> id={}, n={}.".format(str(self.id), str(self.n)))print("\n".join(map(str, range(self.n))))

class Switch(object):"""The class which delegates the jobsbased on the given job id"""def __init__(self, cases):self.cases = cases # dictionary: time complexity for the access operation is 1def resolve(self, id):
try:cases[id].execute()except KeyError as e:print("Given id: {} is wrong!".format(str(id)))


if __name__ == '__main__':
# Casescases=dict([Case1(0, "switch").pair(), Case2(1, 5).pair()])
switch = Switch(cases)
# id will be dynamically specifiedswitch.resolve(0)switch.resolve(1)switch.resolve(2)

我只想在这里透露一下我的意见。Python中没有case/Switch语句的原因是因为Python遵循“做某事只有一种正确的方法”的原则。所以很明显,你可以想出各种方法来重新创建Switch/case功能,但Python实现这一功能的方法是if/elif结构。即,

if something:return "first thing"elif somethingelse:return "second thing"elif yetanotherthing:return "third thing"else:return "default thing"

我只是觉得PEP 8值得在这里点头。Python的美妙之处之一是它的简单和优雅。这在很大程度上源于PEP 8中提出的原则,包括“只有一种正确的方法来做某事”。

我做了一个开关盒实现,它在外部不太使用ifs(它在类中仍然使用如果)。

class SwitchCase(object):def __init__(self):self._cases = dict()
def add_case(self,value, fn):self._cases[value] = fn
def add_default_case(self,fn):self._cases['default']  = fn
def switch_case(self,value):if value in self._cases.keys():return self._cases[value](value)else:return self._cases['default'](0)

像这样使用它:

from switch_case import SwitchCaseswitcher = SwitchCase()switcher.add_case(1, lambda x:x+1)switcher.add_case(2, lambda x:x+3)switcher.add_default_case(lambda _:[1,2,3,4,5])
print switcher.switch_case(1) #2print switcher.switch_case(2) #5print switcher.switch_case(123) #[1, 2, 3, 4, 5]

以下方法适用于我需要一个简单的Switch-case来调用一堆方法而不仅仅是打印一些文本的情况。在玩了lambda和global als之后,它让我觉得这是迄今为止最简单的选择。也许它也会对某人有所帮助:

def start():print("Start")
def stop():print("Stop")
def print_help():print("Help")
def choose_action(arg):return {"start": start,"stop": stop,"help": print_help,}.get(arg, print_help)
argument = sys.argv[1].strip()choose_action(argument)()  # calling a method from the given string

另一种选择:

def fnc_MonthSwitch(int_Month): #### Define a function take in the month variablestr_Return ="Not Found"     #### Set Default Valueif int_Month==1:       str_Return = "Jan"if int_Month==2:       str_Return = "Feb"if int_Month==3:       str_Return = "Mar"return str_Return;          #### Return the month foundprint ("Month Test 3:  " + fnc_MonthSwitch( 3) )print ("Month Test 14: " + fnc_MonthSwitch(14) )

虽然已经有足够的答案,但我想指出一个更简单,更强大的解决方案:

class Switch:def __init__(self, switches):self.switches = switchesself.between = len(switches[0]) == 3
def __call__(self, x):for line in self.switches:if self.between:if line[0] <= x < line[1]:return line[2]else:if line[0] == x:return line[1]return None

if __name__ == '__main__':between_table = [(1, 4, 'between 1 and 4'),(4, 8, 'between 4 and 8')]
switch_between = Switch(between_table)
print('Switch Between:')for i in range(0, 10):if switch_between(i):print('{} is {}'.format(i, switch_between(i)))else:print('No match for {}'.format(i))

equals_table = [(1, 'One'),(2, 'Two'),(4, 'Four'),(5, 'Five'),(7, 'Seven'),(8, 'Eight')]print('Switch Equals:')switch_equals = Switch(equals_table)for i in range(0, 10):if switch_equals(i):print('{} is {}'.format(i, switch_equals(i)))else:print('No match for {}'.format(i))

输出:

Switch Between:No match for 01 is between 1 and 42 is between 1 and 43 is between 1 and 44 is between 4 and 85 is between 4 and 86 is between 4 and 87 is between 4 and 8No match for 8No match for 9
Switch Equals:No match for 01 is One2 is TwoNo match for 34 is Four5 is FiveNo match for 67 is Seven8 is EightNo match for 9

简单,不经过测试;每个条件都独立求值:没有失败,但所有case都会求值(尽管要打开的表达式只求值一次),除非有关掉语句。例如,

for case in [expression]:if case == 1:print(end='Was 1. ')
if case == 2:print(end='Was 2. ')break
if case in (1, 2):print(end='Was 1 or 2. ')
print(end='Was something. ')

打印Was 1. Was 1 or 2. Was something.(该死!为什么我不能在内联代码块中使用尾随空格?),如果expression计算为1,如果expression计算为2,则打印Was 2.,如果expression计算为其他值,则打印Was something.

运行函数的解决方案:

result = {'case1':     foo1,'case2':     foo2,'case3':     foo3,}.get(option)(parameters_optional)

其中fo1()、fo2()和fo3()是函数

例1(带参数):

option = number['type']result = {'number':     value_of_int,  # result = value_of_int(number['value'])'text':       value_of_text, # result = value_of_text(number['value'])'binary':     value_of_bin,  # result = value_of_bin(number['value'])}.get(option)(value['value'])

例2(无参数):

option = number['type']result = {'number':     func_for_number, # result = func_for_number()'text':       func_for_text,   # result = func_for_text()'binary':     func_for_bin,    # result = func_for_bin()}.get(option)()

例4(仅值):

option = number['type']result = {'number':    lambda: 10,       # result = 10'text':      lambda: 'ten',    # result = 'ten''binary':    lambda: 0b101111, # result = 47}.get(option)()

我发现下面的答案来自Python留档

使用if... elif... elif... else序列可以很容易地做到这一点。已经有一些关于Switch语句语法的建议,但是对于是否以及如何进行范围测试还没有达成共识。有关完整的详细信息和当前状态,请参阅PEP 275。

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

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_。如果没有这样的前缀,如果值来自不受信任的来源,攻击者将能够调用您对象上的任何方法。

这里的大多数答案都很古老,尤其是那些被接受的答案,所以似乎值得更新。

首先,官方Python常见问题涵盖了这一点,并推荐elif链用于简单情况,dict用于更大或更复杂的情况。它还为某些情况建议了一组visit_方法(许多服务器框架使用的风格):

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

FAQ还提到了PEP 275,这是为了得到一个关于添加C风格Switch语句的官方一劳永逸的决定而编写的。但该PEP实际上被推迟到Python 3,并且它只是作为一个单独的提案被正式拒绝,PEP 3103。答案当然是否定的-但是如果你对原因或历史感兴趣,这两个PEP可以链接到其他信息。


有一件事多次出现(可以在PEP 275中看到,即使它被作为实际建议删除),如果你真的为8行代码处理4种情况而烦恼,而不是你在C或Bash中的6行,你总是可以这样写:

if x == 1: print('first')elif x == 2: print('second')elif x == 3: print('third')else: print('did not place')

这并没有完全被PEP 8所鼓励,但它是可读的,而不是太单调。


自从PEP 3103被拒绝以来的十多年里,C风格的case语句的问题,甚至是Go中稍微强大一点的版本,都被认为是死的;每当有人在python-的想法或-dev上提出它时,他们都会提到旧的决定。

然而,完全ML风格的模式匹配的想法每隔几年就会出现一次,特别是因为像Swift和Rust这样的语言已经采用了它。问题是如果没有代数数据类型,模式匹配很难得到很大的利用。虽然Guido一直赞同这个想法,但没有人能提出一个非常适合Python的建议。(你可以阅读我的2014稻草人中的例子。)随着3.7中的dataclass和一些零星的建议,这种情况可能会改变,这些建议要求使用更强大的enum来处理sum类型,或者针对不同类型的语句本地绑定的各种建议(比如PEP 3150,或者目前正在讨论的一组建议)。但到目前为止,它还没有。

偶尔也有Perl 6风格匹配的建议,它基本上是从elif到regex到单分派类型切换的所有内容的混杂。

作为Mark Biek的回答的一个小变体,对于像这个重复这样的不常见情况,用户有一堆函数调用要延迟,参数要打包(并且不值得构建一堆脱机的函数),而不是这样:

d = {"a1": lambda: a(1),"a2": lambda: a(2),"b": lambda: b("foo"),"c": lambda: c(),"z": lambda: z("bar", 25),}return d[string]()

你可以这样做:

d = {"a1": (a, 1),"a2": (a, 2),"b": (b, "foo"),"c": (c,)"z": (z, "bar", 25),}func, *args = d[string]return func(*args)

这当然更短,但它是否是更具可读性是一个悬而未决的问题…


我认为对于这种特殊用途,从lambda切换到partial可能更具可读性(尽管不是更简短):

d = {"a1": partial(a, 1),"a2": partial(a, 2),"b": partial(b, "foo"),"c": c,"z": partial(z, "bar", 25),}return d[string]()

…它的优点是可以很好地处理关键字参数:

d = {"a1": partial(a, 1),"a2": partial(a, 2),"b": partial(b, "foo"),"c": c,"k": partial(k, key=int),"z": partial(z, "bar", 25),}return d[string]()

这个答案由abarnert类似,这里有一个解决方案,专门用于为开关中的每个“case”调用单个函数的用例,同时避免lambdapartial以实现超简洁,同时仍然能够处理关键字参数:

class switch(object):NO_DEFAULT = object()
def __init__(self, value, default=NO_DEFAULT):self._value = valueself._result = default
def __call__(self, option, func, *args, **kwargs):if self._value == option:self._result = func(*args, **kwargs)return self
def pick(self):if self._result is switch.NO_DEFAULT:raise ValueError(self._value)
return self._result

示例用法:

def add(a, b):return a + b
def double(x):return 2 * x
def foo(**kwargs):return kwargs
result = (switch(3)(1, add, 7, 9)(2, double, 5)(3, foo, bar=0, spam=8)(4, lambda: double(1 / 0))  # if evaluating arguments is not safe).pick()
print(result)

请注意,这是链接调用,即switch(3)(...)(...)(...)。不要在中间放逗号。将所有内容放在一个表达式中也很重要,这就是为什么我在主调用周围使用额外的括号来隐式行延续。

如果您打开未处理的值,例如switch(5)(1, ...)(2, ...)(3, ...),上述示例将引发错误。您可以提供默认值,例如switch(5, default=-1)...返回-1

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

def decision_time( key, *args, **kwargs):def action1()"""This function is a closure - and has access to all the arguments"""passdef action2()"""This function is a closure - and has access to all the arguments"""passdef action3()"""This function is a closure - and has access to all the arguments"""pass
return {1:action1, 2:action2, 3:action3}.get(key,default)()

这样做的好处是它不会每次都尝试评估函数,你只需要确保外部函数获得内部函数需要的所有信息。

容易记住:

while True:try:x = int(input("Enter a numerical input: "))except:print("Invalid input - please enter a Integer!");if x==1:print("good");elif x==2:print("bad");elif x==3:breakelse:print ("terrible");

到目前为止,已经有很多答案说,“我们在Python中没有Switch,这样做”。然而,我想指出的是,Switch语句本身是一个容易被滥用的构造,在大多数情况下可以而且应该避免,因为它们促进了懒惰编程。

def ToUpper(lcChar):if (lcChar == 'a' or lcChar == 'A'):return 'A'elif (lcChar == 'b' or lcChar == 'B'):return 'B'...elif (lcChar == 'z' or lcChar == 'Z'):return 'Z'else:return None        # or something

现在,你可以使用Switch语句执行此操作(如果Python提供了一个),但你会浪费时间,因为有一些方法可以很好地执行此操作。或者,你有一些不太明显的东西:

def ConvertToReason(code):if (code == 200):return 'Okay'elif (code == 400):return 'Bad Request'elif (code == 404):return 'Not Found'else:return None

但是,这种操作可以而且应该使用字典来处理,因为它会更快,更不复杂,更不容易出错并且更紧凑。

绝大多数Switch语句的“用例”都属于这两种情况中的一种;如果你已经彻底考虑过你的问题,就没有理由使用这种情况。

所以,与其问“我如何在Python中切换?”,也许我们应该问“我为什么要在Python中切换?”因为这通常是一个更有趣的问题,并且经常会暴露出你正在构建的任何设计中的缺陷。

现在,这并不是说开关也不应该被使用。状态机、词法分析器、解析器和自动机都在一定程度上使用它们。一般来说,当你从对称输入开始到非对称输出时,它们会很有用;你只需要确保你不会因为在代码中看到一堆钉子而将开关用作锤子。

def f(x):dictionary = {'a':1, 'b':2, 'c':3}return dictionary.get(x,'Not Found')##Returns the value for the letter x;returns 'Not Found' if x isn't a key in the dictionary

您可以使用分派的字典:

#!/usr/bin/env python

def case1():print("This is case 1")
def case2():print("This is case 2")
def case3():print("This is case 3")

token_dict = {"case1" : case1,"case2" : case2,"case3" : case3,}

def main():cases = ("case1", "case3", "case2", "case1")for case in cases:token_dict[case]()

if __name__ == '__main__':main()

输出:

This is case 1This is case 3This is case 2This is case 1