yokon's blog

优雅的写判断语句

2018.05.20

一个程序之所以能自动做很多任务,是因为我们给他做好了条件判断。项目中,我们难免会使用if/else判断逻辑语句。对简单的情况而言,使用逻辑语句会很清晰简洁,而项目情况一旦复杂,或者判断层次变多,盲目的使用if/else语句就不免会显得难看,而且难以维护和扩展。

比如一个返回今天是什么日子的函数:

def get_today(today):
    if today == 'Monday':
        return '星期一'
    elif today == 'Tuesday':
        return '星期二'
    elif today == 'Wednesday':
        return '星期三'
    elif today == 'Thursday':
        return '星期四'
    elif today == 'Friday':
        return '星期五'
    elif today == 'Saturday':
        return '星期六'
    elif today == 'Sunday':
        return '星期日'
    return '其他'

这样写虽然没有问题,但是如果现在要多加一个节日,这样我们就又要在现有逻辑中去加一条elif逻辑语句。这样发展下去,代码不仅排版冗长难看,而且增加了修改逻辑语句的风险。

表驱动法

表驱动法是一种编写代码的策略,对于复杂的判断逻辑,尽量从表里查找信息而不使用逻辑语句。对于上面的代码,我们可以使用表驱动法,改为:

days = {
    'Monday': '星期一',
    'Tuesday': '星期二',
    'Wednesday': '星期三',
    'Thursday': '星期四',
    'Friday': '星期五',
    'Saturday': '星期六',
    'Sunday': '星期日'
}

def get_today(today):
    if today in days:
        return days.get(today)
    return '其他'

这样我们实际上就将数据和逻辑分离了,好处是数据和逻辑一目了然,维护和扩展更加简单。如果我们需要再加一个节日,只需要在数据中加,不需要去修改逻辑。

days = {
    # ...
    'New Year': '春节'
}

更复杂的嵌套

如果我们现在需要把星期和节日区分开,让他们返回不同的信息。

days = {
    'Monday': '星期一',
    'Tuesday': '星期二',
    'Wednesday': '星期三',
    'Thursday': '星期四',
    'Friday': '星期五',
    'Saturday': '星期六',
    'Sunday': '星期日',
    'New Year': '春节',
    'Birthday': '生日'
}

def get_today(today):
    if today in days:
        result = days.get(today)
        if result.startswith('星期'):
            return '星期几:{}'.format(result)
        elif result.endswith('节'):
            return '节日:{}'.format(result)
        else:
            return '今天是:{}'.format(result)
    else:
        return '其他'

这里我们在if/else中又嵌套了一层判断逻辑,当然这只是一层简单的嵌套,这么写没有问题。而随着项目的演进,功能越来越复杂,如果我们需要在嵌套的逻辑中再加一层嵌套,或者更改这层嵌套逻辑。直接在原有的逻辑中改的话,不免要增大修改的代价和风险。更为重要的是,这对以后的维护和扩展增加了难度。

编辑复杂的项目时,为了使代码逻辑更清晰,更易读易于维护,我们需要对原有的if/else中的嵌套做一个类的封装,封装一个判断的方法(judge())以及一个执行行为方法(action()),它们分别封装了原代码中的if判断和判断后执行的行为。

class TodayIsWeek(object):
    def judge(self, result):
        return result.startswith('星期')
    def action(self, result):
        # do something
class TodayIsFestival(object):
    def judge(self, result):
        return result.endswith('节')
    def action(self, result):
        # do something
class TodayIsOther(object):
    def judge(self, result):
        return not (result.startswith('星期') and result.endswith('节'))
    def action(self, result):
        # do something

对于上面的程序,这么封装肯定是多余的,而如果程序特别复杂,这样做不仅不会显得过度设计,还能让代码结构逻辑更加清晰。我们只需要将上面的封装放到一个表里,遍历它就可以了:

def get_today(today):
    if today in days:
        result = days.get(today)
        # 遍历
        for obj in [TodayIsWeek, TodayIsFestival, TodayIsOther]:
            run = obj()
            if run.judge(result): return run.action()
    else:
        return 'not in days'

最后

在python代码的编写中,我们应该尽量不写过多的逻辑判断嵌套,避免if/else嵌套地狱。对于过于复杂的逻辑判断,尽量对他进行封装,使用表驱动法来分离数据和逻辑,保证可读性同时减少维护代码的成本。