我想在Python中编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch
或case
语句,但Python似乎没有switch
语句。在这种情况下,有哪些推荐的Python解决方案?
我想在Python中编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch
或case
语句,但Python似乎没有switch
语句。在这种情况下,有哪些推荐的Python解决方案?
match
-case
语句,为Python提供了一种一流的实现方式,用于实现"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
语句比这个简单的例子要强大得多。
def f(x):
return {
'a': 1,
'b': 2,
}[x]
get
方法可能比使用collections.defaultdict
更常见。 - Mike Graham}.get(x, default)
语句。请注意:这比在switch语句中没有指定默认值要好得多! - Mike Grahamget(key[, default])
函数:def f(x):
return {
'a': 1,
'b': 2
}.get(x, 9) # 9 will be returned default if x is not found
我一直喜欢用这种方式做
result = {
'a': lambda x: x * 5,
'b': lambda x: x + 7,
'c': lambda x: x - 2
}[value](x)
.get()
方法的方式需要急切地评估所有可能性才能进行分派,因此不仅效率极低(不仅仅是非常低),而且也不能有副作用;该答案解决了这个问题,但更加冗长。我只会使用if/elif/else,即使这些也需要编写与'case'一样长的代码。 - ninjagecko[value]
对字典进行索引,它将仅返回3个函数中的一个(假设value
是其中3个键之一)。此时还没有调用该函数。然后(x)
使用x
作为参数调用刚刚返回的函数(结果存储在result
中)。其他两个函数不会被调用。 - blubberdiblub除了字典方法外(我真的很喜欢,顺便说一句),您还可以使用if
-elif
-else
来实现switch
/case
/default
的功能:
if x == 'a':
# Do the thing
elif x == 'b':
# Do the other thing
if x in 'bc':
# Fall-through by not using elif, but now the default case includes case 'a'!
elif x in 'xyz':
# Do yet another thing
else:
# Do the default
当然,这并不完全等同于switch/case - 你不能像省略break
语句那样轻松地实现贯穿效果,但你可以进行更复杂的测试。尽管在功能上它更接近一系列嵌套的if
语句,但其格式比后者更好。
x = the.other.thing
即可。通常,您会有一个if,多个elif和一个else,因为这更容易理解。 - Matthew Schinckelx in 'bc'
的表达式时,需要注意 "" in "bc"
的结果为 True
。 - Lohmar ASHARPython >= 3.10
哇,Python 3.10+ 现在有一个match
/case
语法,类似于switch/case
等等!
match/case
的选择性特点
1 - 匹配值:
与其他语言中的简单switch/case
类似,可以匹配值:
match something:
case 1 | 2 | 3:
# Match 1-3.
case _:
# Anything else.
#
# If `case _:` is omitted, an error will be thrown
# if `something` doesn't match any of the patterns.
2-匹配结构模式:
match something:
case str() | bytes():
# Match a string like object.
case [str(), int()]:
# Match a `str` and an `int` sequence
# (A sequence can be a `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.
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
我最喜欢的 Python switch/case 的方法是:
choices = {'a': 1, 'b': 2}
result = choices.get(key, 'default')
// 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'))
default=-1; result=choices.get(key, default)
来增加可读性。 - ChaimGresult = key=='a'?1:key==b?2:-1
。 - Jasenresult = 1 if key == 'a' else (2 if key == 'b' else 'default')
。但是这个一行式的代码可读性如何呢? - ChaimGclass switch(object):
value = None
def __new__(class_, value):
class_.value = value
return True
def case(*args):
return any((arg == switch.value for arg in args))
用法:
while switch(n):
if case(0):
print "You typed zero."
break
if case(1, 4, 9):
print "n is a perfect square."
break
if case(2):
print "n is an even number."
if case(2, 3, 5, 7):
print "n is a prime number."
break
if case(6, 8):
print "n is an even number."
break
print "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.
case(2)
块调用另一个使用switch()的函数,则在执行case(2,3,5,7)
等查找下一个要执行的case时,它将使用由其他函数设置的switch值而不是当前switch语句设置的值。 - user9876我最喜欢的一个是一个非常好的代码片段。它最接近实际的switch case语句,特别是在功能方面。
class switch(object):
def __init__(self, value):
self.value = value
self.fall = False
def __iter__(self):
"""Return the match method once, then stop"""
yield self.match
raise StopIteration
def match(self, *args):
"""Indicate whether or not to enter a case suite"""
if self.fall or not args:
return True
elif self.value in args: # changed for v1.5, see below
self.fall = True
return True
else:
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 1
break
if case('two'):
print 2
break
if case('ten'):
print 10
break
if case('eleven'):
print 11
break
if 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 empty
if case('b'): pass
# ...
if case('y'): pass
if case('z'):
print "c is lowercase!"
break
if case('A'): pass
# ...
if case('Z'):
print "c is uppercase!"
break
if case(): # default
print "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 string
c = 'A'
for case in switch(c):
if case(*string.lowercase): # note the * for unpacking as arguments
print "c is lowercase!"
break
if case(*string.uppercase):
print "c is uppercase!"
break
if case('!', '?', '.'): # normal argument passing style also applies
print "c is a sentence terminator!"
break
if case(): # default
print "I dunno what c was!"
一些评论指出,使用with foo as case
而不是for case in foo
的上下文管理器解决方案可能更加简洁,对于大型开关语句,线性而不是二次行为可能是一个不错的选择。此答案中for循环的价值部分在于具有break和fallthrough的能力,如果我们愿意稍微调整关键字的选择,我们也可以在上下文管理器中实现这一点:
class Switch:
def __init__(self, value):
self.value = value
self._entered = False
self._broken = False
self._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 False
self._entered, self._prev = True, values
return True
if self._prev is None:
self._prev = values
return True
if self._prev != values:
self._broken = True
return False
if self._prev == values:
self._prev = None
return False
@property
def 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')
break
while case.default:
print('default')
break
with switch() as case
替换 for case in switch()
,因为它只需要运行一次,更有意义。 - Skiif c in set(range(0,9)): print "digit" elif c in set(map(chr, range(ord('a'), ord('z')))): print "lowercase"
- mpagclass 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 switch
print("I hate mondays so much.")
# Note there is no break needed here
elif 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 here
print("Let's go have fun!") # Didn't use case for example purposes
value
属性,以便您可以在语句中引用case.value
。 - Peter我从Twisted Python代码中学到了一种模式。
class SMTP:
def lookupMethod(self, command):
return getattr(self, 'do_' + command.upper(), None)
def do_HELO(self, rest):
return 'Howdy ' + rest
def 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_UNKNOWN
if 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)
)
运行函数的解决方案:
result = {
'case1': foo1,
'case2': foo2,
'case3': foo3,
}.get(option)(parameters_optional)
其中foo1()、foo2()和foo3()是函数。
示例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)()
foo2()
,那么 foo1()
、foo3()
和 default()
函数也会运行,这意味着事情可能需要很长时间。 - Brian Underwood