背景
假如有一个课程学习管理系统,其中有个表的字段叫 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”即可收费取得