如何摆脱一堆“如果”

2024-10-03 04:27:22 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑到这项任务:

你开得有点太快了,警察拦住了你。编写代码来计算结果,编码为int值:0=无票,1=小票,2=大票。如果速度小于等于60,则结果为0。如果速度介于61和80之间(包括61和80),则结果为1。如果速度大于等于81,则结果为2。你知道吗

首先想到的是一堆假设,比如:

def caught_speeding(speed):
  if speed > 80:
      return 2
  elif speed > 60:
      return 1          
  else:
      return 0

有没有办法用更少的行数达到同样的效果?你知道吗


Tags: 代码编码returnifdefelse速度int
3条回答
>>> from bisect import bisect_left
>>> bisect_left([60, 80], 50)
0
>>> bisect_left([60, 80], 60)
0
>>> bisect_left([60, 80], 70)
1
>>> bisect_left([60, 80], 80)
1
>>> bisect_left([60, 80], 90)
2

所以你可以

def caught_speeding(speed):
    bisect_left([60, 80], speed)

由于使用bisect进行线性搜索,因此速度不如使用bisect那么快,但下面对间隔或指定间隔的顺序(也没有对正在比较的值类型(可以是整数、浮点数、日期等)做一些假设:

import sys

def caught_speeding(speed, intervals):
    for i, interval in enumerate(intervals):
        if interval[0] <= speed <= interval[1]:
            return i
    return None  # wasn't in any interval

speed_ranges = [[0, 60], [61, 80], [81, sys.maxint]]

print(caught_speeding(45, speed_ranges))  #  > 0
print(caught_speeding(65, speed_ranges))  #  > 1
print(caught_speeding(90, speed_ranges))  #  > 2

解决这个问题的一般方法是使用binary search;您可以使用^{} module实现一个:

import bisect

def caught_speeding(speed):
   tickets = [60, 80]
   return bisect.bisect_left(tickets, speed)

bisect_left函数以最有效的方式为您的速度找到插入点(它将查看每个列表的一半,以查看您的速度应该适合的位置,然后再找到一半,直到找到插入点为止)。对于速度高达60的情况,将其插入位置0,对于速度介于61和80之间的情况,将其插入位置1,其余的将添加到末尾的位置2:

>>> import bisect
>>> tickets = [60, 80]
>>> bisect.bisect_left(tickets, 60)
0
>>> bisect.bisect_left(tickets, 61)
1
>>> bisect.bisect_left(tickets, 80)
1
>>> bisect.bisect_left(tickets, 81)
2

对于3种不同的票面价值,这确实让人感觉有些过分。稍后回到代码时,您的3分支if-elif-else构造更容易理解。你知道吗

相关问题 更多 >