场景:消除条件分支

使用 bisect 优化范围类分支判断

1
2
3
4
5
6
7
8
9
10
11
12
def rank(rating):
rating_num = float(rating)
if rating_num >= 8.5:
return 'S'
elif rating_num >= 8:
return 'A'
elif rating_num >= 7:
return 'B'
elif rating_num >= 6:
return 'C'
else:
return 'D'

要优化这段代码,我们得把所有分界点收集起来,放在一个元组里。因为 breakpoints 已经是一个排好序的元组,所以我们可以直接使用 bisect 模块来实现查找功能:

1
2
3
4
5
6
7
8
import bisect

def rank(rating):
breakpoints = (6, 7, 8, 8.5)
grades = ('D', 'C', 'B', 'A', 'S')

index = bisect.bisect(breakpoints, float(rating))
return grades[index]

使用字典优化分支代码

1
2
3
4
5
6
7
8
9
10
11
12
def get_sorted_movies(movies, sorting_type):
if sorting_type == 'name':
sorted_movies = sorted(movies, key=lambda movie: movie.name.lower())
elif sorting_type == 'rating':
sorted_movies = sorted(movies, key=lambda movie: float(movie.rating), reverse=True)
elif sorting_type == 'year':
sorted_movies = sorted(movies, key=lambda movie: movie.year, reverse=True)
elif sorting_type == 'random':
sorted_movies = sorted(movies, key=lambda movie: random.random())
else:
raise RuntimeError(f'Unknown sorting type: {sorting_type}')
return sorted_movies

Python 的字典可以装下任何对象,所以我们可以把各个分支下不同的东西——排序的 key 函数和 reverse 参数,直接放进算法字典里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_sorted_movies(movies, sorting_type):
sorting_algos = {
'name': (lambda movie: movie.name.lower(), False),
'rating': (lambda movie: float(movie.rating), True),
'year': (lambda movie: movie.year, True),
'random': (lambda movie: random.random(), False),
}
try:
key_func, reverse = sorting_algos[sorting_type]
except KeyError:
raise RuntimeError(f'Unknown sorting type: {sorting_type}')

sorted_movies = sorted(movies, key=key_func, reverse=reverse)
return sorted_movies

在分支中寻找多态的应用时机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class FancyLogger:
_redis_max_length = 1024

def __init__(self, output_type=OutputType.FILE):
self.output_type = output_type

def log(self, message):
if self.output_type == OutputType.FILE:
pass
elif self.output_type == OutputType.REDIS:
pass
elif self.output_type == OutputType.ES:
pass
else:
raise TypeError('output type invalid')

def pre_process(self, message):
# Redis 对日志最大长度有限制,需要进行裁剪
if self.output_type == OutputType.REDIS:
return message[:self._redis_max_length]

多态表示同一个方法调用,在运行时会因为对象类型的不同,产生不同效果。比起把所有的分支和可能性,一股脑儿地塞进程序员的脑子里,多态思想驱使我们更积极地寻找有效的抽象,以此隔离各个模块,让它们之间通过规范的接口来通信:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class FancyLogger:
def __init__(self, output_writer=None):
self._writer = output_writer or FileWriter()

def log(self, message):
self._writer.write(message)

class FileWriter:
def write(self, message):
pass

class RedisWriter:
max_length = 1024

def _pre_process(self, message):
# Redis 对日志最大长度有限制,需要进行裁剪
return message[:self.max_length]

def write(self, message):
message = self._pre_process(message)
pass

class EsWriter:
def write(self, message):
pass