0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

寫好Python代碼一定要知道的幾條重要技巧

Linux愛好者 ? 來源:NightTeam ? 作者:韋世東 ? 2021-05-28 15:58 ? 次閱讀

程序設(shè)計(jì)的好與壞,早在我們青蔥歲月時(shí)就接觸過了,只是那是并不知道這竟如此重要。能夠立即改善程序設(shè)計(jì)、寫出“好”代碼的知識(shí)有以下幾點(diǎn):

?面向?qū)ο笪鍌€(gè)基本原則;?常見的三種架構(gòu);?繪圖;?起一個(gè)好名字;?優(yōu)化嵌套的 if else 代碼;

當(dāng)然,其他技術(shù)知識(shí)的豐富程度也決定了程序設(shè)計(jì)的好壞。例如通過引入消息隊(duì)列解決雙端性能差異問題、通過增加緩存層提高查詢效率等。下面我們一起來看看,上面列出的知識(shí)點(diǎn)包含哪些內(nèi)容,這些內(nèi)容對(duì)代碼和程序設(shè)計(jì)的改善有何幫助。

面向?qū)ο笪鍌€(gè)基本原則

本書作者是 2010 級(jí)學(xué)生,面向?qū)ο笫亲髡咔嗍[時(shí)期發(fā)展火熱的編程范式。它的五個(gè)基本原則是:

?單一職責(zé)原則;?開放封閉原則;?依賴倒置原則;?接口隔離原則;?合成復(fù)用原則;

下面我們將通過對(duì)比和場(chǎng)景假設(shè)的方式了解五個(gè)基本原則對(duì)代碼質(zhì)量的影響。

立竿見影的單一職責(zé)原則

沒錯(cuò),立竿見影、效果卓越。對(duì)于我們這些自學(xué)編程無師自通的人來說,能把功能實(shí)現(xiàn)就可以了,根本沒有時(shí)間考慮代碼優(yōu)化和維護(hù)成本問題。時(shí)光流逝,竟在接觸編程很長(zhǎng)一段時(shí)間后才發(fā)現(xiàn)它竟如此重要。

俗話說只要代碼寫的夠爛,提升就足夠明顯。以一個(gè)從文件內(nèi)容中匹配關(guān)鍵數(shù)據(jù)并根據(jù)匹配結(jié)果發(fā)出網(wǎng)絡(luò)請(qǐng)求的案例,看看大部分程序員的寫法:

import re import requests FILE = “。/information.fet” def extract(file): fil = open(file, “r”) content = fil.read() fil.close() find_object = re.search(r“url=d+”, content) find = find_object.group(1) text = requests.get(find) return text if __name__ == “__main__”: text = extract(FILE) print(text)

需求已經(jīng)實(shí)現(xiàn),這點(diǎn)毋庸置疑,但是問題來了:

?如果讀取文件的時(shí)候發(fā)生異常了怎么辦??如果數(shù)據(jù)源發(fā)生變化該如何處理??如果網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù)不符合最終要求怎么辦?

如果你心里的第一個(gè)反應(yīng)是改代碼,那你就要注意了。完成一件事中間的某個(gè)環(huán)節(jié)發(fā)生變化,改代碼是在所難免的,但是如果按照上面這種寫法,不僅代碼越改越亂,連邏輯也會(huì)越來越亂。單一職責(zé)原則表達(dá)的是讓一個(gè)函數(shù)盡量只做一件事,不要將多件事混雜在一個(gè)函數(shù)中。

上面的代碼如果重新設(shè)計(jì),我認(rèn)為至少應(yīng)該是這樣的:

def get_source(): “”“獲取數(shù)據(jù)源”“” return def extract_(val): “”“匹配關(guān)鍵數(shù)據(jù)”“” return def fetch(val): “”“發(fā)出網(wǎng)絡(luò)請(qǐng)求”“” return def trim(val): “”“修剪數(shù)據(jù)”“” return def extract(file): “”“提取目標(biāo)數(shù)據(jù)”“” source = get_source() content = extract_(source) text = trim(fetch(content)) return text if __name__ == “__main__”: text = extract(FILE) print(text)

把原來放在一個(gè)函數(shù)中實(shí)現(xiàn)的多個(gè)步驟拆分成為多個(gè)更小的函數(shù),每個(gè)函數(shù)只做一件事。當(dāng)數(shù)據(jù)源發(fā)生變化時(shí),只需要改動(dòng) get_source 相關(guān)的代碼即可;如果網(wǎng)絡(luò)請(qǐng)求返回的數(shù)據(jù)不符合最終要求,我們可以在 trim 函數(shù)中對(duì)它進(jìn)行修剪。這樣一來,代碼應(yīng)對(duì)變化的能力提高了許多,整個(gè)流程也變得更清晰易懂。改動(dòng)前后的變化如下圖所示:

051751b8-be65-11eb-9e57-12bb97331649.png

單一職責(zé)原則的核心是解耦和增強(qiáng)內(nèi)聚力,如果一個(gè)函數(shù)承擔(dān)的職責(zé)過多,等于把這些職責(zé)耦合在一起,這種耦合會(huì)導(dǎo)致脆弱的設(shè)計(jì)。當(dāng)發(fā)生變化時(shí),原本的設(shè)計(jì)會(huì)遭受到意想不到的破壞。單一職責(zé)原則實(shí)際上是把一件事拆分成多個(gè)步驟,代碼修改造成的影響范圍很小。

讓代碼穩(wěn)定性飛升的開放封閉原則和依賴倒置原則

開放封閉原則中的開放指的是對(duì)擴(kuò)展開放,封閉指的是對(duì)修改封閉。需求總是變化的,業(yè)務(wù)方這個(gè)月讓你把數(shù)據(jù)存儲(chǔ)到 MySQL 數(shù)據(jù)庫中,下個(gè)月就有可能讓你導(dǎo)出到 Excel 表格里,這時(shí)候你就得改代碼了。這個(gè)場(chǎng)景和上面的單一職責(zé)原則很相似,同樣面臨代碼改動(dòng),單一職責(zé)原則示例主要表達(dá)的是通過解耦降低改動(dòng)的影響,這里主要表達(dá)的是通過對(duì)擴(kuò)展開放、對(duì)修改封閉提高程序應(yīng)對(duì)變化的能力和提高程序穩(wěn)定性。

穩(wěn)定這個(gè)詞如何理解呢?

較少的改動(dòng)或者不改動(dòng)即視為穩(wěn)定,穩(wěn)定意味著調(diào)用這個(gè)對(duì)象的其它代碼拿到的結(jié)果是可以確定的,整體是穩(wěn)定的。

按照一般程序員的寫法,數(shù)據(jù)存儲(chǔ)的代碼大概是這樣的:

class MySQLSave: def __init__(self): pass def insert(self): pass def update(self): pass class Business: def __init__(self): pass def save(self): saver = MySQLSave() saver.insert()

功能是能夠?qū)崿F(xiàn)的,這點(diǎn)毋庸置疑。來看看它如何應(yīng)對(duì)變化,如果要更換存儲(chǔ),那么就意味著需要改代碼。按照上面的代碼示例,有兩個(gè)選擇:

?重新寫一個(gè)存儲(chǔ)到 ExcelSave 的類;?對(duì) MySQLSave 類進(jìn)行改動(dòng);

上面的兩種選擇,無論怎么選都會(huì)改動(dòng) 2 個(gè)類。因?yàn)椴粌H存儲(chǔ)的類需要改動(dòng),調(diào)用處的代碼也需要更改。這樣一來,它們整體都是不穩(wěn)定的。如果換一種實(shí)現(xiàn)方式,根據(jù)依賴倒置的設(shè)計(jì)指導(dǎo)可以輕松應(yīng)對(duì)這個(gè)問題。邊看代碼邊理解:

import abc class Save(metaclass=abc.ABCMeta): @abc.abstractmethod def insert(self): pass @abc.abstractmethod def update(self): pass class MySQLSave(Save): def __init__(self): self.classify = “mysql” pass def insert(self): pass def update(self): pass class Excel(Save): def __init__(self): self.classify = “excel” def insert(self): pass def update(self): pass class Business: def __init__(self, saver): self.saver = saver def insert(self): self.saver.insert() def update(self): self.saver.update() if __name__ == “__main__”: mysql_saver = MySQLSave() excel_saver = Excel() business = Business(mysql_saver)

這里通過內(nèi)置的 abc 實(shí)現(xiàn)了一個(gè)抽象基類,這個(gè)基類的目的是強(qiáng)制子類實(shí)現(xiàn)要求的方法,以達(dá)到子類功能統(tǒng)一。子類功能統(tǒng)一后,無論調(diào)用它的哪個(gè)子類,都是穩(wěn)定的,不會(huì)出現(xiàn)調(diào)用方還需要修改方法名或者修改傳入參數(shù)的情況。

依賴倒置中的倒置,指的是依賴關(guān)系的倒置。之前的代碼是調(diào)用方 Business 依賴對(duì)象 MySQLSave,一旦對(duì)象 MySQLSave 需要被替換, Business 就需要改動(dòng)。依賴倒置中的依賴指的是對(duì)象的依賴關(guān)系,之前依賴的是實(shí)體,如果改為后面這種依賴抽象的方式,情況就會(huì)扭轉(zhuǎn)過來:

05325cb0-be65-11eb-9e57-12bb97331649.png

實(shí)體 Business 依賴抽象有一個(gè)好處:抽象穩(wěn)定。相對(duì)于多變的實(shí)體來說,抽象更穩(wěn)定。代碼改動(dòng)前后的依賴關(guān)系發(fā)生了重大變化,之前調(diào)用方 Business 直接依賴于實(shí)體 MySQLSave,通過依賴倒置改造后 Busines 和 ExcelSave、 MySQLSave 全都依賴抽象。

這樣做的好處是如果需要更換存儲(chǔ),只需要?jiǎng)?chuàng)建一個(gè)新的存儲(chǔ)實(shí)體,然后調(diào)用 Business 時(shí)傳遞進(jìn)去即可,這樣可以不用改動(dòng) Business 的代碼,符合面向修改封閉、面向擴(kuò)展開放的開放封閉原則;

依賴倒置的具體實(shí)現(xiàn)方式使用了一種叫做依賴注入的手段,實(shí)際上單純使用依賴注入、不使用依賴倒置也可以滿足開閉原則要求,感興趣的讀者不妨試一試。

挑肥揀瘦的接口隔離原則

接口隔離原則中的接口指的是Interface,而不是 Web 應(yīng)用里面的 Restful 接口,但是在實(shí)際應(yīng)用中可以將其抽象理解為相同的對(duì)象。接口隔離原則在設(shè)計(jì)層面看,跟單一職責(zé)原則的目的是一致的。接口隔離原則的指導(dǎo)思想是:

?調(diào)用方不應(yīng)該依賴它不需要的接口;?依賴關(guān)系應(yīng)當(dāng)建立在最小接口上;

這實(shí)際上是告訴我們要給接口減肥,過多功能的接口可以選用拆分的方式優(yōu)化。舉個(gè)例子,現(xiàn)在為圖書館設(shè)計(jì)一個(gè)圖書的抽象類:

import abc class Book(metaclass=abc.ABCMeta): @abc.abstractmethod def buy(self): pass @abc.abstractmethod def borrow(self): pass @abc.abstractmethod def shelf_off(self): pass @abc.abstractmethod def shelf_on(self): pass

圖可以被購(gòu)買、可以被借閱、可以下架、可以上架,這看起來并沒有什么問題。但這樣一來這個(gè)抽象只能提供給管理人員使用,用戶操作時(shí)需要再設(shè)定一個(gè)新的抽象類,因?yàn)槟悴豢赡茏層脩艨梢圆倏v圖書上下架。接口隔離原則推薦的做法是把圖書的上下架和圖書購(gòu)買、借閱分成 2 個(gè)抽象類,管理端的圖書類繼承 2 個(gè)抽象類,用戶端的圖書類繼承 1 個(gè)抽象類。這么看起來是有點(diǎn)繞,不要慌,我們看圖理解:

054fdcea-be65-11eb-9e57-12bb97331649.png

這樣是不是一下就看懂了。這個(gè)指導(dǎo)思想很重要,不僅能夠指導(dǎo)我們?cè)O(shè)計(jì)抽象接口,也能夠指導(dǎo)我們?cè)O(shè)計(jì) Restful 接口,還能夠幫助我們發(fā)現(xiàn)現(xiàn)有接口存在的問題,從而設(shè)計(jì)出更合理的程序。

輕裝上陣的合成復(fù)用原則

合成復(fù)用原則的指導(dǎo)思想是:盡量使用對(duì)象組合,而不是繼承來達(dá)到復(fù)用的目的。合成復(fù)用的作用是降低對(duì)象之間的依賴,因?yàn)槔^承是強(qiáng)依賴關(guān)系,無論子類使用到父類的哪幾個(gè)屬性,子類都需要完全擁有父類。合成采用另一種方式實(shí)現(xiàn)對(duì)象之間的關(guān)聯(lián),降低依賴關(guān)系。

為什么推薦優(yōu)先使用合成復(fù)用,而后考慮繼承呢?

因?yàn)槔^承的強(qiáng)依賴關(guān)系,一旦被依賴的對(duì)象(父類)發(fā)生改變,那么依賴者(子類)也需要改變,合成復(fù)用則可以避免這樣的情況出現(xiàn)。要注意的是,推薦優(yōu)先使用復(fù)用,但并不是拒絕使用繼承,該用的地方還得用。我們以一段代碼為例,說明合成復(fù)用和繼承的差異:

import abc class Car: def move(self): pass def engine(self): pass class KateCar(Car): def move(self): pass def engine(self): pass class FluentCar(Car): def move(self): pass def engine(self): pass

這里的 Car 作為父類,擁有 move 和 engine 2 個(gè)重要屬性,這時(shí)候如果需要給汽車涂裝顏色,那么就要新增一個(gè) color 屬性,3 個(gè)類都要增加。如果使用合成復(fù)用的方式,可以這么寫:

class Color: pass class KateCar: color = Color() def move(self): pass def engine(self): pass class FluentCar: color = Color() def move(self): pass def engine(self): pass

類對(duì)象合成復(fù)用的具體操作是在類中實(shí)例化一個(gè)類對(duì)象,然后在需要的時(shí)候調(diào)用它。代碼可能沒有那么直觀,我們看圖:

這個(gè)例子主要用于說明繼承和合成復(fù)用的具體實(shí)現(xiàn)方式和前后變化,對(duì)于 Car 的繼承無需深究,因?yàn)槿绻銏?zhí)著地討論為什么右圖中的 2 個(gè) Car 不用繼承,就會(huì)陷入牛角尖。

這里的合成復(fù)用選用的實(shí)現(xiàn)方式是在 2 個(gè) Car 里面實(shí)例化另一個(gè)類 Color,其實(shí)也可以用依賴注入的手段在外部實(shí)例化 Color,然后把實(shí)例對(duì)象傳遞給 2 個(gè) Car。

常見的三種架構(gòu)

了解多種不同的架構(gòu)可以使我們的知識(shí)面更寬廣,面對(duì)一類問題的時(shí)候可以提出其它解決辦法。同時(shí),了解多種架構(gòu)可以讓我們?cè)谠O(shè)計(jì)階段做好規(guī)劃,避免后續(xù)頻繁的重構(gòu)。常見的三種架構(gòu)分別是:

?單體架構(gòu);?分布式架構(gòu);?微服務(wù)架構(gòu);

單體架構(gòu)

單體架構(gòu)是我們平時(shí)接觸較多的架構(gòu),也是相對(duì)容易理解的架構(gòu)。單體架構(gòu)把所有功能都聚合在一個(gè)應(yīng)用里,我們可以簡(jiǎn)單地將這種架構(gòu)視作:

0579189e-be65-11eb-9e57-12bb97331649.png

這種架構(gòu)簡(jiǎn)單、容易部署和測(cè)試,大部分應(yīng)用的初期都采用單體架構(gòu)。單體架構(gòu)也有幾個(gè)明顯缺點(diǎn):

?復(fù)雜性高,所有功能糅合在一個(gè)應(yīng)用里,模塊多、容易出現(xiàn)邊界模糊,而且隨著時(shí)間的推移和業(yè)務(wù)的發(fā)展,項(xiàng)目越來越大、代碼越來越多,整體服務(wù)效率逐漸下降;?發(fā)布/部署頻率低,牽一發(fā)而動(dòng)全身,新功能或問題修復(fù)的發(fā)布上線需要多方協(xié)調(diào),發(fā)布時(shí)間一拖再拖。

項(xiàng)目大則構(gòu)建時(shí)間長(zhǎng)、構(gòu)建失敗的幾率也會(huì)有所增加;?性能瓶頸明顯,一頭牛再厲害也抵不過多頭牛合力的效果,隨著數(shù)據(jù)量、請(qǐng)求并發(fā)的增加,讀性能的不足最先暴露出來,接著你就會(huì)發(fā)現(xiàn)其它方面也跟不上了;?影響技術(shù)創(chuàng)新:?jiǎn)误w架構(gòu)通常選用一類語言或一類框架統(tǒng)一開發(fā),想要引入新技術(shù)或者接入現(xiàn)代化的服務(wù)是很困難的;?可靠性低,一旦服務(wù)出現(xiàn)問題,影響是巨大的。

分布式架構(gòu)

分布式架構(gòu)相對(duì)于單體架構(gòu)而言,通過拆分解決了單體架構(gòu)面臨的大部分問題,例如性能瓶頸。假如單體架構(gòu)是一頭牛,那么分布式架構(gòu)就是多頭牛:

當(dāng)單體架構(gòu)出現(xiàn)性能瓶頸時(shí),團(tuán)隊(duì)可以考慮將單體架構(gòu)轉(zhuǎn)換為分布式架構(gòu),以增強(qiáng)服務(wù)能力。當(dāng)然,分布式并不是萬能的,它解決了單體架構(gòu)性能瓶頸、可靠性低的問題,但復(fù)雜性問題、技術(shù)創(chuàng)新問題和發(fā)布頻率低依然存在,這時(shí)候可以考慮微服務(wù)。

微服務(wù)架構(gòu)

微服務(wù)架構(gòu)的關(guān)鍵字是拆,將原本糅合在一個(gè)應(yīng)用中的多個(gè)功能拆成多個(gè)小應(yīng)用,這些小應(yīng)用串聯(lián)起來組成一個(gè)與之前單體架構(gòu)功能相同的完整應(yīng)用。具體示意如下:

059e2d28-be65-11eb-9e57-12bb97331649.png

每個(gè)微服務(wù)可以獨(dú)立運(yùn)行,它們之間通過網(wǎng)絡(luò)協(xié)議進(jìn)行交互。每個(gè)微服務(wù)可以部署多個(gè)實(shí)例,這樣一來就具備了跟分布式架構(gòu)相同的性能。單個(gè)服務(wù)的發(fā)布/部署對(duì)其它服務(wù)的影響較小,在代碼上沒有關(guān)聯(lián),因此可以頻繁的發(fā)布新版本。復(fù)雜性的問題迎刃而解,拆分之后架構(gòu)邏輯清晰、功能模塊職責(zé)單一,功能的增加和代碼的增加也不會(huì)影響到整體效率。服務(wù)獨(dú)立之后,項(xiàng)目就變得語言無關(guān),評(píng)價(jià)服務(wù)可以用 Java 語言來實(shí)現(xiàn)也可以用 Golang 語言實(shí)現(xiàn),不再受到語言或者框架的制約,技術(shù)創(chuàng)新問題得以緩解。

這是不是很像單一職責(zé)原則和接口隔離原則?

分布式和微服務(wù)并不是銀彈

從上面的對(duì)比來看,似乎分布式架構(gòu)比單體架構(gòu)好,微服務(wù)架構(gòu)比分布式架構(gòu)好,這么說來微服務(wù)架構(gòu)》分布式架構(gòu)》單體架構(gòu)?

這么理解是不對(duì)的,架構(gòu)需要根據(jù)場(chǎng)景和需求選擇,微服務(wù)架構(gòu)和分布式架構(gòu)看上去很美,但也衍生出了許多新問題。以微服務(wù)架構(gòu)為例:

?運(yùn)維成本高,在單體架構(gòu)時(shí),運(yùn)維只需要保證 1 個(gè)應(yīng)用正常運(yùn)行即可,關(guān)注的可能只是硬件資源消耗問題。但如果換成微服務(wù)架構(gòu),應(yīng)用的數(shù)量成百上千,當(dāng)應(yīng)用出現(xiàn)問題或者多應(yīng)用之間協(xié)調(diào)異常時(shí),運(yùn)維人員的頭就會(huì)變大;?分布式系統(tǒng)固有的復(fù)雜性,網(wǎng)絡(luò)分區(qū)、分布式事務(wù)、流量均衡對(duì)開發(fā)者和運(yùn)維進(jìn)行了敲打;

?接口調(diào)整成本高,一個(gè)接口的調(diào)用方可能有很多個(gè),如果設(shè)計(jì)時(shí)沒有遵循開放封閉原則和接口隔離原則,那么調(diào)整的工作量會(huì)是非常大的;

?接口性能受限,原本通過函數(shù)調(diào)用的方式交互,在內(nèi)存中很快就完成了,換成接口后通過網(wǎng)絡(luò)進(jìn)行交互,性能明顯下降;

?重復(fù)勞動(dòng),雖然有公共模塊,但如果語言無關(guān)且又要考慮性能(不用接口交互)就需要自己實(shí)現(xiàn)一份相同功能的代碼;

到底用哪種架構(gòu),需要根據(jù)具體的場(chǎng)景來選擇。如果你的系統(tǒng)復(fù)雜度并沒有那么高、性能追求也沒有那么高,例如一個(gè)日數(shù)據(jù)量只有幾萬的爬蟲應(yīng)用,單體架構(gòu)就足以解決問題,不需要強(qiáng)行做成分布式或者微服務(wù),因?yàn)檫@樣只會(huì)增加自己的工作量。

畫好圖

在需要表達(dá)關(guān)系和邏輯梳理的場(chǎng)景里,圖永遠(yuǎn)比代碼好。業(yè)內(nèi)流行這么一句話“程序開發(fā),設(shè)計(jì)先行”,說的是在開發(fā)前,需要對(duì)程序進(jìn)行構(gòu)思和設(shè)計(jì)。試想,如果連對(duì)象關(guān)系和邏輯都說不清楚,寫出的代碼會(huì)是好代碼嗎?

在構(gòu)思項(xiàng)目時(shí)可以使用用例圖挖掘需求和功能模塊;在架構(gòu)設(shè)計(jì)時(shí)可以使用協(xié)作圖梳理模塊關(guān)系;在設(shè)計(jì)接口或者類對(duì)象時(shí)可以使用類圖做好交互計(jì)劃;在功能設(shè)計(jì)時(shí)可以使用狀態(tài)圖幫助我們挖掘功能屬性……

了解繪圖的重要性之后,具體的繪圖方法和技巧可翻閱本書(《Python 編程參考》)的工程師繪圖指南章節(jié)展開學(xué)習(xí)。

起一個(gè)好名字

你還記得自己曾經(jīng)起過的那些名字嗎:

?reversalList?get_translation?get_data?do_trim?CarAbstract

起一個(gè)好的、合適的名字能夠讓代碼風(fēng)格更統(tǒng)一,看上去清晰了然。起一個(gè)好名字不單單是單詞語法的問題,還會(huì)涉及風(fēng)格選擇和用途。具體的命名方法和技巧可翻閱本書(《Python 編程參考》)的命名選擇與風(fēng)格指南章節(jié)展開學(xué)習(xí)。

優(yōu)化嵌套的 if else 代碼

寫代碼的時(shí)候用一些控制語句是很正常的事,但是如果 if else 嵌套太多,也是非常頭疼的,代碼看上去就像下面這樣。

這種結(jié)構(gòu)的產(chǎn)生是因?yàn)槭褂昧?if 語句來進(jìn)行先決條件的檢查,如果負(fù)責(zé)條件則進(jìn)入下一行代碼,如果不符合則停止。既然這樣,那我們?cè)谙葲Q條件的檢查上進(jìn)行取反即可,代碼改動(dòng)過后看起來像這樣:

if “http” not in url: return if “www” not in url: return這是我平時(shí)常用的優(yōu)化辦法,后來在張曄老師的付費(fèi)專欄中看到這種手段的名稱——衛(wèi)語句。

當(dāng)然,這種簡(jiǎn)單的邏輯處理和 if else 控制流用衛(wèi)語句進(jìn)行處理是很有效的,但如果邏輯再?gòu)?fù)雜一些,用衛(wèi)語句的效果就不見的那么好了。假設(shè)汽車 4S 店有折扣權(quán)限限制,普通銷售有權(quán)對(duì) 30 萬以內(nèi)金額的汽車授予一定折扣,超過 30 萬但在 80 萬以內(nèi)需要精英銷售進(jìn)行授權(quán),更高價(jià)格的車折扣需要店長(zhǎng)授權(quán)。這個(gè)功能可以歸納為根據(jù)金額的大小來確定授權(quán)者,對(duì)應(yīng)代碼如下:

def buying_car(price): if price 《 300000: print(“普通銷售”) elif price 《 800000: print(“精英銷售”) elif price 《 1500000: print(“店長(zhǎng)”)

代碼思路清晰,但存在的問題也明顯。如果以后擴(kuò)展價(jià)格和定級(jí),會(huì)增加更多的 if else 語句,代碼將變得臃腫??刂普Z句的順序是固定在代碼中的,如果想要調(diào)整順序,只能修改控制語句。

那么問題來了,有比 if else 更合適的辦法嗎?

這時(shí)候可以考慮一種叫做責(zé)任鏈的設(shè)計(jì)模式,責(zé)任鏈設(shè)計(jì)模式的定義為:為了避免請(qǐng)求發(fā)送者與多個(gè)請(qǐng)求處理者耦合在一起,于是將所有請(qǐng)求的處理者通過前一對(duì)象記住其下一個(gè)對(duì)象的引用而連成一條鏈;當(dāng)有請(qǐng)求發(fā)生時(shí),可將請(qǐng)求沿著這條鏈傳遞,直到有對(duì)象處理它為止。

看起來有點(diǎn)繞,我們通過代碼圖加深理解:

處理類執(zhí)行前根據(jù)先決條件判斷自身的 handler 是否能夠處理,如果不能則交給 next_handler,也就是責(zé)任鏈的下一個(gè)節(jié)點(diǎn)。上面的用責(zé)任鏈實(shí)現(xiàn)為:

class Manager: def __init__(self,): self.obj = None def next_handler(self, obj): self.obj = obj def handler(self, price): pass class General(Manager): def handler(self, price): if price 《 300000: print(“{} 普通銷售”.format(price)) else: self.obj.handler(price) class Elite(Manager): def handler(self, price): if 300000 《= price 《 800000: print(“{} 精英銷售”.format(price)) else: self.obj.handler(price) class BOSS(Manager): def handler(self, price): if price 》= 800000: print(“{} 店長(zhǎng)”.format(price))

創(chuàng)建好抽象類和具體的處理類之后,它們還沒有關(guān)聯(lián)關(guān)系。我們需要將它們掛載到一起,成為一個(gè)鏈條:

general = General() elite = Elite() boss = BOSS() general.next_handler(elite) elite.next_handler(boss)

這里建立的責(zé)任鏈順序?yàn)?General -》 Elite -》 BOSS,調(diào)用方只需要傳遞價(jià)格給 General,如果它沒有折扣的授予權(quán)它會(huì)交給 Elite 處理,如果 Elite 沒有折扣授予權(quán)則會(huì)交給 BOSS 處理。對(duì)應(yīng)代碼如下:

prices = [550000, 220000, 1500000, 200000, 330000] for price in prices: general.handler(price)

這跟我們?nèi)?4S 店購(gòu)車是一樣的,作為客戶,我們確定好要買的車即可,至于 4S 店如何申請(qǐng)折扣,誰來授權(quán)與我無關(guān),我能拿到相應(yīng)的折扣即可。

至此,if else 優(yōu)化知識(shí)學(xué)習(xí)完畢。

做到以上幾點(diǎn),相信你的代碼質(zhì)量和程序設(shè)計(jì)水平會(huì)有一個(gè)不錯(cuò)的提升,加油!

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴

原文標(biāo)題:寫好 Python 代碼的幾條重要技巧

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    終于知道為什么一定要預(yù)埋HDMI線了

    ] | | ---------------------------------------------------------------------------------------- |蕞近,大數(shù)據(jù)給我推送了篇文章,講的是家裝時(shí)一定要預(yù)埋HDMI線,我在想大家在裝
    的頭像 發(fā)表于 10-24 15:25 ?111次閱讀

    貼片電容型號(hào)除了要知道參數(shù)規(guī)格外還有哪些要知道

    在選擇貼片電容型號(hào)時(shí),除了要知道其參數(shù)規(guī)格(如尺寸、容量、電壓、精度等)外,還需要考慮以下幾個(gè)方面。
    的頭像 發(fā)表于 09-21 14:58 ?227次閱讀

    4G模組無法正常聯(lián)網(wǎng)?一定要記得考慮SIM卡的問題!

    當(dāng)大家在調(diào)試4G模組但卻無法正常聯(lián)網(wǎng)時(shí), 大多數(shù)人的第反應(yīng)是這4G模組一定有什么問題吧? 幾乎沒有人會(huì)認(rèn)為是流量卡(SIM卡)的問題,一定要記得考慮SIM卡。
    的頭像 發(fā)表于 08-12 15:37 ?1439次閱讀
    4G模組無法正常聯(lián)網(wǎng)?<b class='flag-5'>一定要</b>記得考慮SIM卡的問題!

    液位變送器的優(yōu)點(diǎn)你一定要知道哦!

    液位變送器
    華泰天科
    發(fā)布于 :2024年08月09日 11:40:49

    一定要知道的音叉液位開關(guān)原理

    液位計(jì)
    jzyb
    發(fā)布于 :2024年06月28日 10:17:08

    pcb設(shè)計(jì)的基本原則分享 PCB設(shè)計(jì)16個(gè)原則一定要知道

    PCB設(shè)計(jì)的這16個(gè)原則你一定要知道
    的頭像 發(fā)表于 03-12 11:19 ?2633次閱讀

    這些關(guān)于直線電機(jī)模組選型要求與原則,你一定要知道

    直線電機(jī)模組作為現(xiàn)代工業(yè)自動(dòng)化領(lǐng)域中的重要組成部分,其選型要求十分嚴(yán)格。在進(jìn)行直線電機(jī)模組選型時(shí),需要遵循一定的要求和原則,以確保選型的合理性和適用性。
    的頭像 發(fā)表于 01-17 11:30 ?818次閱讀
    這些關(guān)于直線電機(jī)模組選型要求與原則,你<b class='flag-5'>一定要知道</b>

    制板人要知道的pcb icd是什么意思

    制板人要知道的pcb icd是什么意思
    的頭像 發(fā)表于 12-04 15:56 ?1849次閱讀

    python軟件IDLE怎么打多行代碼

    用于編寫、編輯和運(yùn)行Python代碼的編輯器窗口。在IDLE中編寫多行代碼有幾種方法可以實(shí)現(xiàn)。 使用括號(hào)與換行符: 在IDLE中編寫多行代碼
    的頭像 發(fā)表于 11-29 15:00 ?3829次閱讀

    python軟件怎么運(yùn)行代碼

    Python種高級(jí)編程語言,它被廣泛用于開發(fā)各種類型的應(yīng)用程序,從簡(jiǎn)單的腳本到復(fù)雜的網(wǎng)絡(luò)應(yīng)用和機(jī)器學(xué)習(xí)模型。要運(yùn)行Python代碼,您需要
    的頭像 發(fā)表于 11-28 16:02 ?843次閱讀

    python如何換行而不運(yùn)行代碼

    和可讀性。 在Python中,可以使用兩個(gè)主要的方法進(jìn)行換行:使用反斜杠()和使用圓括號(hào)(())。 第種方式是使用反斜杠()來表示換行。在Python中,反斜杠是個(gè)轉(zhuǎn)義字符,它可以
    的頭像 發(fā)表于 11-24 09:50 ?3043次閱讀

    python代碼寫完后點(diǎn)哪個(gè)運(yùn)行

    當(dāng)你完成了編寫Python代碼后,你可以選擇多種方式來運(yùn)行它。下面是幾種常見的運(yùn)行代碼的方式: Python解釋器:Python
    的頭像 發(fā)表于 11-24 09:28 ?4858次閱讀

    python如何直循環(huán)個(gè)代碼

    Python中,有幾種方法可以實(shí)現(xiàn)代碼的循環(huán)執(zhí)行。下面我將詳盡、詳實(shí)、細(xì)致地介紹這些方法和它們的使用情況。 使用while循環(huán): 在Python中,可以使用while循環(huán)來重復(fù)執(zhí)行
    的頭像 發(fā)表于 11-23 15:54 ?2428次閱讀

    python變量命名規(guī)則

    的規(guī)則和約定。本文將詳盡、詳實(shí)、細(xì)致地探討Python變量的命名規(guī)則,幫助讀者了解如何正確命名變量并在編程中遵循最佳實(shí)踐。 、變量命名規(guī)則的重要性 合適的變量命名對(duì)于編寫清晰、易讀和易于維護(hù)的
    的頭像 發(fā)表于 11-23 15:44 ?1163次閱讀

    python怎樣運(yùn)行代碼

    討論Python代碼的運(yùn)行方式,包括解釋器、交互式環(huán)境和命令行。 Python代碼可以通過兩種主要的方式運(yùn)行:解釋執(zhí)行和編譯執(zhí)行。解釋執(zhí)行是指將源
    的頭像 發(fā)表于 11-22 10:31 ?1121次閱讀