条件分支控制流高级用法

举报
brucexiaogui 发表于 2022/08/30 22:51:11 2022/08/30
【摘要】 条件分支控制流高级用法 1.概述 这篇文章总结条件判断的一些高级用法,总结下条件分支控制流使用策略。 在编程中能不用条件就不用能不嵌套条件表达式就不要嵌套能简化条件表达式就不要复杂化条件表达式。 ...

条件分支控制流高级用法

1.概述

这篇文章总结条件判断的一些高级用法,总结下条件分支控制流使用策略。

  • 在编程中能不用条件就不用
  • 能不嵌套条件表达式就不要嵌套
  • 能简化条件表达式就不要复杂化条件表达式。

2.条件控制流高级用法

2.1.省略零值判断

当你编写if分支时,如果需要判断某个类型的对象是否是零值,可能会把代码写成下面。

if containers_count == 0

if fruits_list != []

  
 
  • 1
  • 2
  • 3

这种判断语句其实可以变得更简单,因为某个对象作为主角出现在if分支里,解释器会主动对它进行“真值测试”,也就是调用bool()函数获取他的布尔值。
每类对象都有着各自的规则。
布尔值为假:None、0、False、[]、()、{}、set()、frozenset()、等
布尔值为真:非0的数值、True、非空的列表、元组、字典、用户定义的类和实例等等

if not containers_count:

if not fruits_list

  
 
  • 1
  • 2
  • 3

2.2.把否定逻辑移入表达式内

在构造布尔逻辑表达式时,你可以用not关键字来表达否定含义。

i > 8
not i>8

  
 
  • 1
  • 2

有时候过于使用not关键字,反倒忘记了运算符本身就可以表达否定逻辑。这样的代码,就好比你在看到一个人沿着楼梯往上走时,不说“他在上楼”,非要说“他在做和下楼相反的事情”

if not number < 10
if not index == 1

  
 
  • 1
  • 2

如果把否定逻辑移入表达式内,他们可以改成下面这样。

if number >= 10
if current_user is not None

  
 
  • 1
  • 2

2.3.尽可能让三元表达式保持简单

除了标准的分支,python提供了浓缩的表达式,三元表达式

# 三元表达式语法
true_value if <expression> else false_value

  
 
  • 1
  • 2

2.4.修改对象的布尔值

class UserCollection:
    def __init__(self, users):
        self.items = users

users = UserCollection(['xiaoming', 'xiaozhang'])
if len(users.items) > 0:
   print(f'这个属性有值:{users.items}')

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面的示例判断user对象是否真的有内容,因此在分支判断语句用到了len(users.items) > 0 这样的表达式,其实这个可以变得更简单。
只要给UserCollection类实现__len__魔法方法,users对象就可以直接用于“真值测试”

class userCollection_v2:
    def __init__(self, users):
        self.items = users
    # 为类定义len魔法方法,python在计算这类对象的布尔值时,会受len(users)的结果影响,假如长度为0,布尔值为False,反之为True
    def __len__(self):
        return len(self.items)

users = userCollection_v2(['xiaoming', 'xiaozhang'])
# 不在需要手动判断对象内部items的长度
if users:
    print(f'这个属性有值:{users.items}')

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

不过定义len并非影响布尔值结果的唯一办法,还有一个魔法方法__bool__和对象的布尔值息息相关。
为对象定义__bool__方法后,对它进行布尔值运算会直接返回该方法的调用结果。

class ScoreJudger:
    def __init__(self, score):
        self.score = score

    # 仅当分数大于60时为真
    def __bool__(self):
        return self.score >= 60
if ScoreJudger(60):
    print('true')
else:
    print('false')

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

如果一个类中同时定义了__len__和__bool__两个方法,解释器会优先使用__bool__方法的执行结果。

2.5.判断对象None时用is运算符

当我们判断两个对象是否相等时,可以使用双等号和is来判断,但是他们是有区别的。因此我们需要了解它的区别在根据场景选择使用哪个方式。

  • 双等号对比两个对象的值是否相等,行为可被__eq__方法重载
  • is判断两个对象指向的内存地址是否是同一个,无法被重载。当在执行x is y时,其实就是在判断id(x)和id(y)的结果是否相等。

1.双等号计算两个对象是否相等

# is与==
x,y,z = 1, 1, 2
print('x == y的结果:', x == y)
print('x == z的结果:', x == z)

# 重写eq方法
class EqualWithAnything:
    def __eq__(self, other):
        '''
        判断 x == y
        :param other: y的值
        :return:
        '''
        return True
foo = EqualWithAnything()
print('重写eq魔法方法后,上面定义的EqualWithAnything对象,在和任何对象做==计算时都会返回True。', foo == None)

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.is计算两个对象是否相等

当你想判断对象是否为None时,应该使用is运算符,它的行为不会被重写。

print('is判断对象是否为None', foo is None)
# 输出结果
False

x = None
print('有且仅有真正的None 才能通过is判断', x is None)
# 输出结果
True

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

既然is在进行比较时更严格,为什么不把所有判断都用is来替代那?
这是因为,除了None,True,False这三个内置对象以外,其他类型对象在python中并不是严格以单例模式存在的,即便值一致他们在内存中仍然可以是两个对象。
因此当你需要判断某个对象是否是None,True,False时,使用is,其他情况下使用 ==

2.6.消失的分支

从一份电影数据中提取电影信息,按照评分rating的值把电影划分为S、A、B、C、等级;按照指定顺序输出电影信息。

1.电影分析工具第一个版本

import random

movies = [{'name': '侠客行', 'year': 2008, 'rating': '9'},
          {'name': '西游记', 'year': 1983, 'rating': '9'},
          {'name': '厨神', 'year': 2010, 'rating': '7'},
          {'name': '喜来乐', 'year': 2006, 'rating': '6.9'},
          {'name': '康熙来了', 'year': 2021, 'rating': '8'}
          ]


class Movies:
    def __init__(self, name, year, rating):
        self.name = name
        self.year = year
        self.rating = rating

    @property
    def rank(self):
        '''
        根据评分对电影分级
        -S:8.5分以上
        -A: 8~8.5
        -B: 7~8
        -C: 6~7
        -D: 6分以下
        '''
        rating_num = float(self.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'


def get_scorted_movies(movies, sorting_type):
    '''
    电影列表排序并返回结果
    :param movies: 对象列表
    :param sorting_type: 排序选项
    :return:
    '''
    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


def main():
    '''
    为了把上面代码串起来,在main函数里实现了接收排序选项,解析电影数据,排序并打印电影列表功能。
    :return:
    '''
    all_sorting_type = ('name', 'rating', 'year', 'random')
    sorting_type = input('Please input sorting type:')
    if sorting_type not in all_sorting_type:
        print('Sorry,"{}" is not a valid sorting type,please choose from'
              '"{}",exit now'.format(sorting_type, '/'.join(all_sorting_type))
              )
        return

    # 初始化电影数据对象
    movies_items = []
    for movie_json in movies:
        movie = Movies(**movie_json)
        movies_items.append(movie)

    # 排序输出电影列表
    sorted_movies = get_scorted_movies(movies_items,sorting_type)
    for movie in sorted_movies:
        print(
            f'-[{movie.rank} {movie.name} ({movie.year}) | rating: {movie.rating}]'
        )


if __name__ == '__main__':
    main()

# 输出结果
Please input sorting type:name
-[S 侠客行 (2008) | rating: 9]
-[C 厨神 (2010) | rating: 7]
-[C 喜来乐 (2006) | rating: 6.9]
-[B 康熙来了 (2021) | rating: 8]
-[S 西游记 (1983) | rating: 9]

  
 
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

上面的代码完成了一个小工具,虽然这个工具实现了功能,在他的代码里隐藏着两大段可以简化的条件分支代码,如果使用前档的方式可以使分支消失。

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

第一个需要优化的分支判断是rank方法属性中的分支。

@property
    def rank(self):
        '''
        根据评分对电影分级
        -S:8.5分以上
        -A: 8~8.5
        -B: 7~8
        -C: 6~7
        -D: 6分以下
        '''
        rating_num = float(self.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'

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

仔细观察这段代码,你会发现它有一个明细的规律,每个if/elif 语句后,都跟着一个评分的分界点,这个分界点把评分分成不同的分段。要优化这段代码,先把所有分界点收集起来,放在一个元组里。

breakpoints = [6, 7, 8, 8.5]

  
 
  • 1

breakpoints已经是一个排好序的元组,所以我们可以直接使用bisect模块来实现查找功能,bisect是python内置的二分算法模块,他有一个同名函数bisect,可以用来在有序列表里做二分查找。

import bisect
# 用二分查找的容器必须是排好序的
breakpoints = [6, 7, 8, 8.5]

print('使用二分查找算法,根据值返回索引位置:', bisect.bisect(breakpoints, 7))

# 将分界点定义成元组,并引入bisect,之前的分支代码可以简化成下面的样子。
class Movies2:
    def __init__(self, name, year, rating):
        self.name = name
        self.year = year
        self.rating = rating

    @property
    def rank(self):
        breakpoints = (6, 7, 8, 8.5)
        grades = ('D', 'C', 'B', 'A', 'S')
        index = bisect.bisect(breakpoints,float(self.rating))
        return grades[index]


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3.使用字典优化k-v相等类型的分支

在get_sorted_movies()函数里,同样有一段分支代码,这段代码有两个明显的特点。

  • 他用到的条件表达式都非常类似,都是对sorting_type做等值判断也就是key==value。
  • 每个分支内部逻辑也大同小异,都是调用sorted()函数,只是key和reverse略有不同。
    如果有一段条件分支代码同时满足这两个条件,我们就可以使用字典来简化它。
def get_scorted_movies(movies, sorting_type):
    '''
    电影列表排序并返回结果
    :param movies: 对象列表
    :param sorting_type: 排序选项
    :return:
    '''
    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

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

文章来源: brucelong.blog.csdn.net,作者:Bruce小鬼,版权归原作者所有,如需转载,请联系作者。

原文链接:brucelong.blog.csdn.net/article/details/126570606

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。