我用這個(gè)操作,代碼可讀性提升一個(gè)檔次
↑ 關(guān)注 + 星標(biāo) ,每天學(xué)Python新技能
后臺(tái)回復(fù)【大禮包】送你Python自學(xué)大禮包
背景
假設(shè)有一個(gè)課程學(xué)習(xí)管理系統(tǒng),其中有個(gè)表的字段叫status,用來(lái)標(biāo)記學(xué)員某個(gè)課程的學(xué)習(xí)狀態(tài), 它在數(shù)據(jù)庫(kù)中的類型是int。
現(xiàn)在我們的業(yè)務(wù)系統(tǒng)中充斥著大量諸如status==xxx這樣的代碼:
if status==0:
pass
elif status==1:
pass
...
if finished:
status =2
問(wèn)題
相信你看到問(wèn)題所在了,作為一個(gè)剛接手項(xiàng)目的人來(lái)說(shuō),你肯定會(huì)疑惑這里的0、1、2、3這些值到底代表什么狀態(tài),你不得不去代碼中查找status是怎么定義的,有哪些值,分別代表什么意思?
如果文檔中沒有寫清楚這些值對(duì)應(yīng)的含義,那你還要去找最開始寫這個(gè)代碼的人(內(nèi)心你肯定會(huì)罵這是哪個(gè)傻瓜寫的)
所以這段代碼最大的問(wèn)題就是可讀性差,甚至即使作者自己也難說(shuō)隔一段時(shí)間再來(lái)看不會(huì)懵逼。
重構(gòu)
那么我們?nèi)绾蝸?lái)重構(gòu)這段代碼來(lái)提升可讀性呢?,有幾種重構(gòu)方案,接下來(lái)我會(huì)把這幾種方法都列出來(lái)
第一種:使用枚舉
枚舉(Enum)是Python自帶的特性,用來(lái)定義有多個(gè)有限值時(shí)是絕佳的應(yīng)用場(chǎng)景。先定義了狀態(tài)枚舉類
import enum
class StudyStatusEnum(enum.Enum):
no_start = 0 # 未開始
learning = 1 # 學(xué)習(xí)中
finish = 2 # 已學(xué)完
expired = 3 # 過(guò)期
然后將前面的邏輯修改成:
if status==StudyStatusEnum.no_start:
pass
elif status==StudyStatusEnum.leanring:
pass
if finished:
status = StudyStatusEnum.finish
如果你的系統(tǒng)中用的ORM框架是SQLAlchemy,那么要同時(shí)修改status屬性的定義
class Study(Models):
# status = db.Column(db.SmallInteger, default=0, comment="學(xué)習(xí)狀態(tài) 0 未開始 1 學(xué)習(xí)中 2 已完成")
status = db.Column(Enum(StudyStatusEnum), default=StudyStatusEnum.no_start)
這樣修改其實(shí)有個(gè)問(wèn)題是,枚舉類型在數(shù)據(jù)庫(kù)中對(duì)應(yīng)的是字符串類型,而實(shí)際上我們希望存一個(gè)int類型,對(duì)應(yīng)枚舉的value值,而不是枚舉的name屬性。
所以,這時(shí)候我們可以基于SQLAlchemy提供的TypeDecorator來(lái)自定義Enum類型叫做IntEnum。
from sqlalchemy import TypeDecorator, Integer
class IntEnum(TypeDecorator):
"""
整數(shù)枚舉類型 主要用于狀態(tài)等場(chǎng)景
"""
impl = Integer
def __init__(self, enumtype, *args, **kwargs):
super().__init__(*args, **kwargs)
self._enumtype = enumtype
def process_bind_param(self, value, dialect):
# 入庫(kù)時(shí)調(diào)用此方法,返回的是枚舉的value
return value.value
def process_result_value(self, value, dialect):
# 從數(shù)據(jù)庫(kù)加載到內(nèi)存時(shí)的值,返回的一個(gè)枚舉實(shí)例
return self._enumtype(value)
我們只需要重寫這三個(gè)方法,即可已實(shí)現(xiàn)一個(gè)可以保存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
這種也能提高可讀性,不過(guò)有個(gè)問(wèn)題是,如果有很多這樣的字段,不僅有學(xué)習(xí)狀態(tài),還有考試狀態(tài),如果每個(gè)狀態(tài)都有好幾個(gè)值,那么我們會(huì)定義很多個(gè)類似的常量值。
而實(shí)際上這4個(gè)值都應(yīng)該是歸為一類的,就是學(xué)習(xí)狀態(tài),那么我們能不能歸類處理呢?
答案就是用 namedtuple
第三種:使用namedtuple
namedtuple 是一個(gè)具名元組,就是一個(gè)帶有名字的元組,元組中的每個(gè)元素都對(duì)應(yīng)有一個(gè)名字,是不是非常適合用在這種場(chǎng)景?而且比用類和枚舉來(lái)表示更輕量級(jí)。
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
這樣我們只需要定義一個(gè)枚舉常量對(duì)象,里面有4個(gè)值,分別代表4種不同的狀態(tài)。namedtuple 可以認(rèn)為是一個(gè)更輕量級(jí)的類,只有屬性沒有方法。
用namedtuple來(lái)重構(gòu),而且還不需要修改Model層面的字段屬性,改動(dòng)最小,卻大大提高了可讀性。
以上就是一個(gè)通過(guò)用namedtuple提高代碼可讀性的實(shí)踐案例??纯茨愕拇a中是不是充斥著這樣的代碼,也可以試著重構(gòu)一下。


