我用这个操作,代码可读性提升一个档次

2022/08/05 11:14
阅读数 15

背景
假设有一个课程学习管理系统,其中有个表的字段叫status,用来标记学员某个课程的学习状态, 它在数据库中的类型是int。

现在我们的业务系统中充斥着大量诸如status==xxx这样的代码:

if status==0:
    pass



elif status==1:
    pass



...



if  finished:
    status =2

问题
相信你看到问题所在了,作为一个刚接手项目的人来说,你肯定会疑惑这里的0、1、2、3这些值到底代表什么状态,你不得不去代码中查找status是怎么定义的,有哪些值,分别代表什么意思?

如果文档中没有写清楚这些值对应的含义,那你还要去找最开始写这个代码的人(内心你肯定会骂这是哪个傻瓜写的)

所以这段代码最大的问题就是可读性差,甚至即使作者自己也难说隔一段时间再来看不会懵逼。

重构
那么我们如何来重构这段代码来提升可读性呢?,有几种重构方案,接下来我会把这几种方法都列出来

第一种:使用枚举
枚举(Enum)是Python自带的特性,用来定义有多个有限值时是绝佳的应用场景。先定义了状态枚举类

import enum
class StudyStatusEnum(enum.Enum):
    no_start = 0  # 未开始

    learning = 1  # 学习中

    finish = 2  # 已学完

    expired = 3 # 过期

然后将前面的逻辑修改成:

if status==StudyStatusEnum.no_start:
    pass



elif status==StudyStatusEnum.leanring:
    pass



if  finished:
    status = StudyStatusEnum.finish

如果你的系统中用的ORM框架是SQLAlchemy,那么要同时修改status属性的定义

class Study(Models):
    # status = db.Column(db.SmallInteger, default=0, comment="学习状态 0 未开始 1 学习中 2 已完成")

    status = db.Column(Enum(StudyStatusEnum), default=StudyStatusEnum.no_start)

这样修改其实有个问题是,枚举类型在数据库中对应的是字符串类型,而实际上我们希望存一个int类型,对应枚举的value值,而不是枚举的name属性。

所以,这时候我们可以基于SQLAlchemy提供的TypeDecorator来自定义Enum类型叫做IntEnum。

from sqlalchemy import TypeDecorator, Integer
class IntEnum(TypeDecorator):
    """

    整数枚举类型 主要用于状态等场景



    """

    impl = Integer


    def __init__(self, enumtype, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._enumtype = enumtype


    def process_bind_param(self, value, dialect):
        # 入库时调用此方法,返回的是枚举的value

        return value.value


    def process_result_value(self, value, dialect):
        # 从数据库加载到内存时的值,返回的一个枚举实例

        return self._enumtype(value)

我们只需要重写这三个方法,即可已实现一个可以保存int类型的枚举。

status = db.Column(IntEnum(StudyStatusEnum), default=StudyStatusEnum.no_start)

第二种:使用常量
直接将status所有可能的值用常量值代替

NO_FINISH = 0

LEARNING = 1

FINISH = 2

EXPRIED = 3




if status== NO_FINISH:
    pass



elif status==LEARNING:
    pass



if  finished:
    status =FINISH

这种也能提高可读性,不过有个问题是,如果有很多这样的字段,不仅有学习状态,还有考试状态,如果每个状态都有好几个值,那么我们会定义很多个类似的常量值。

而实际上这4个值都应该是归为一类的,就是学习状态,那么我们能不能归类处理呢?

答案就是用 namedtuple

第三种:使用namedtuple
namedtuple 是一个具名元组,就是一个带有名字的元组,元组中的每个元素都对应有一个名字,是不是非常适合用在这种场景?而且比用类和枚举来表示更轻量级。

from collections import namedtuple


StudyStatus = namedtuple("StudyStatus", "no_start, learning, finish, end")
STUDY_STATUS = StudyStatus(0, 1, 2, 3)



if status== STUDY_STATUS.no_start:
    pass



elif status==STUDY_STATUS.learing:
    pass



if  finished:
    status =STUDY_STATUS.finish

这样我们只需要定义一个枚举常量对象,里面有4个值,分别代表4种不同的状态。namedtuple 可以认为是一个更轻量级的类,只有属性没有方法。

用namedtuple来重构,而且还不需要修改Model层面的字段属性,改动最小,却大大提高了可读性。

以上就是一个通过用namedtuple提高代码可读性的实践案例。看看你的代码中是不是充斥着这样的代码,也可以试着重构一下。

最近整理了几百 G 的 Python 学习资料,包含新手入门电子书、教程、源码等等,免费分享给大家!想要的前往 “Python 编程学习圈”,发送 “J” 即可免费获得

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部