Python编写switch case的有效方法比较

Ken*_*nny 5 python switch-statement

我通常使用字典实现 Switch/Case 进行相等比较。

dict = {0:'zero', 1:'one', 2:'two'}; 
a=1; res = dict[a]
Run Code Online (Sandbox Code Playgroud)

代替

if a==0 :
  res = 'zero'
elif a == 1:
  res = 'one'
elif a==2:
  res = 'two'
Run Code Online (Sandbox Code Playgroud)

是否有策略来实施类似的非等比较方法?

if score <=10 :
  cat = 'A'
elif score >10 and score <=30:
  cat = 'B'
elif score >30 and score <=50 :
  cat = 'C'
elif score >50 and score <=90 :
  cat = 'D'
else:
  cat = 'E'
Run Code Online (Sandbox Code Playgroud)

我知道使用 <、<=、>、>= 可能很棘手,但是是否有任何策略可以概括它或从列表中生成自动语句

{[10]:'A', [10,30]:'B', [30,50]:'C',[50,90]:'D',[90]:'E'}
Run Code Online (Sandbox Code Playgroud)

还有一些标志来说明它是 < 还是 <=

Ala*_* T. 5

字典可以包含很多值,如果您的范围不是太宽,您可以通过以编程方式扩展每个范围来制作一个类似于相等条件的字典:

from collections import defaultdict

ranges   = {(0,10):'A', (10,30):'B', (30,50):'C',(50,90):'D'}
valueMap = defaultdict(lambda:'E')
for r,letter in ranges.items(): 
    valueMap.update({ v:letter for v in range(*r) })

valueMap[701] # 'E'
valueMap[7] # 'A'
Run Code Online (Sandbox Code Playgroud)

您也可以从 if/elif 语句中删除多余的条件并对其进行稍微不同的格式化。这几乎看起来像一个 case 语句:

if   score < 10 : cat = 'A'
elif score < 30 : cat = 'B'
elif score < 50 : cat = 'C'
elif score < 90 : cat = 'D'
else            : cat = 'E'
Run Code Online (Sandbox Code Playgroud)

为了避免重复得分 < 您可以定义一个 case 函数并将其与值一起使用:

score = 43
case = lambda x: score < x
if   case(10): cat = "A"
elif case(30): cat = "B"
elif case(50): cat = "C"
elif case(90): cat = "D"
else         : cat = "E"
print (cat) # 'C'
Run Code Online (Sandbox Code Playgroud)

您可以通过创建一个 switch 函数来概括这一点,该函数返回一个“case”函数,该函数适用于具有通用比较模式的测试值:

def switch(value):
    def case(check,lessThan=None):
        if lessThan is not None:
            return (check is None or check <= value) and value < lessThan
        if type(value) == type(check): return value == check
        if isinstance(value,type(case)): return check(value)
        return value in check
    return case
Run Code Online (Sandbox Code Playgroud)

这个通用版本允许各种组合:

score = 35
case = switch(score)
if   case(0,10)         : cat = "A"
elif case([10,11,12,13,14,15,16,17,18,19]): 
                          cat = "B"
elif score < 30         : cat = "B" 
elif case(30) \
  or case(range(31,50)) : cat = 'C'
elif case(50,90)        : cat = 'D'
else                    : cat = "E"
print(cat) # 'C'
Run Code Online (Sandbox Code Playgroud)

当您需要做的只是返回一个值时,还有另一种使用 lambda 函数的方法:

score = 41
case  = lambda x,v: v if score<x else None
cat   = case(10,'A') or case(20,'B') or case(30,'C') or case(50,'D') or 'E' 
print(cat) # "D"
Run Code Online (Sandbox Code Playgroud)

最后一个也可以使用列表推导式和映射表来表示:

mapping = [(10,'A'),(30,'B'),(50,'C'),(90,'D')]
scoreCat = lambda s: next( (L for x,L in mapping if s<x),"E" )

score = 37
cat = scoreCat(score) 
print(cat) #"D"
Run Code Online (Sandbox Code Playgroud)

[编辑] 更具体地说,可以使用设置函数创建通用解决方案,该函数根据您的参数返回映射函数:

def rangeMap(*breaks,inclusive=False):
    default = breaks[-1] if len(breaks)&1 else None
    breaks  = list(zip(breaks[::2],breaks[1::2]))
    def mapValueLT(value):
        return next( (tag for tag,bound in breaks if value<bound), default)
    def mapValueLE(value):
        return next( (tag for tag,bound in breaks if value<=bound), default)
    return mapValueLE if inclusive else mapValueLT

scoreToCategory = rangeMap('A',10,'B',30,'C',50,'D',90,'E')

print(scoreToCategory(53)) # D
print(scoreToCategory(30)) # C

scoreToCategoryLE = rangeMap('A',10,'B',30,'C',50,'D',90,'E',inclusive=True)

print(scoreToCategoryLE(30)) # B
Run Code Online (Sandbox Code Playgroud)

请注意,通过更多的工作,您可以使用 bisect 模块提高返回函数的性能


Thi*_*ker 5

Python 3.10 引入match- case(基本上 switch)你可以将它用作

def check_number(no):
  match no:
    case 0:
      return 'zero'
    case 1:
      return 'one'
    case 2:
      return 'two'
    case _:
      return "Invalid num"
Run Code Online (Sandbox Code Playgroud)

这是我尝试过的一个例子。 在此输入图像描述


Oli*_*çon 3

bisect模块可用于此类分类问题。特别是,该文档提供了一个示例,它解决了与您的问题非常相似的问题。

这是适合您的用例的相同示例。该函数返回两个值:字母等级和bool指示匹配是否精确的标志。

from bisect import bisect_left

grades = "ABCDE"
breakpoints = [10, 30, 50, 90, 100]

def grade(score):
          index = bisect_left(breakpoints, score)
          exact = score == breakpoints[index]
          grade = grades[index]
          return grade, exact

grade(10) # 'A', True
grade(15) # 'B', False
Run Code Online (Sandbox Code Playgroud)

在上面,我假设你的最后一个断点100E. 如果您确实不需要上限,请注意您可以替换100math.inf以保持代码正常运行。