我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
我想用Python编写一个函数,根据输入索引的值返回不同的固定值。
在其他语言中,我会使用switch或case语句,但Python似乎没有switch语句。在此场景中,推荐的Python解决方案是什么?
当前回答
Python>=3.10
哇,Python 3.10+现在有一个match/case语法,类似于switch/case等等!
PEP 634——结构模式匹配
匹配/案例的选定特征
1-匹配值:
匹配值类似于另一种语言中的简单开关/大小写:
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或HTML)时非常有用,这些数据可能以多种不同的模式之一出现。
捕获变量是一项功能。但这也意味着您只需要使用虚线常量(例如: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'))
其他回答
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
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]
我从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”)。这被标记为['HELO','foo.bar.com']。实际的方法查找名称取自部件[0]。
(原始方法也称为state_COMMAND,因为它使用相同的模式来实现状态机,即getattr(self,'state_'+self.mode))
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 be
provided through the derived class
specific 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" with
the instance on which "execute" will be called
"""
return (self.id, self)
def execute(self): # Base class virtual method that needs to be overridden
pass
class Case1(Case):
def __init__(self, id, msg):
self.id = id
self.msg = msg
def execute(self): # Override the base class method
print("<Case1> id={}, message: \"{}\"".format(str(self.id), self.msg))
class Case2(Case):
def __init__(self, id, n):
self.id = id
self.n = n
def execute(self): # Override the base class method
print("<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 jobs
based on the given job id
"""
def __init__(self, cases):
self.cases = cases # dictionary: time complexity for the access operation is 1
def resolve(self, id):
try:
cases[id].execute()
except KeyError as e:
print("Given id: {} is wrong!".format(str(id)))
if __name__ == '__main__':
# Cases
cases=dict([Case1(0, "switch").pair(), Case2(1, 5).pair()])
switch = Switch(cases)
# id will be dynamically specified
switch.resolve(0)
switch.resolve(1)
switch.resolve(2)
虽然已经有了足够的答案,但我想指出一个更简单、更强大的解决方案:
class Switch:
def __init__(self, switches):
self.switches = switches
self.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 0
1 is between 1 and 4
2 is between 1 and 4
3 is between 1 and 4
4 is between 4 and 8
5 is between 4 and 8
6 is between 4 and 8
7 is between 4 and 8
No match for 8
No match for 9
Switch Equals:
No match for 0
1 is One
2 is Two
No match for 3
4 is Four
5 is Five
No match for 6
7 is Seven
8 is Eight
No match for 9