博客專欄

        EEPW首頁 > 博客 > 獨家 | Python中的SOLID原則(附鏈接)

        獨家 | Python中的SOLID原則(附鏈接)

        發(fā)布人:數(shù)據派THU 時間:2021-09-19 來源:工程師 發(fā)布文章

        作者:Mattia Cinelli

        翻譯:朱啟軒

        校對:歐陽錦

        SOLID原則是由Robert C. Martin提出的以首字母縮寫命名的編碼準則,它代表了五種不同的編碼習慣。

        如果您遵循這些原則,您就可以通過完善代碼的結構和邏輯來提高代碼的可靠度。

        1.png

        Photo by ThisisEngineering RAEng on Unsplash

        以下是SOLID的五大原則:

        The Single-Responsibility Principle (SRP)

        單一任務原則

        The Open-Closed Principle (OCP)

        開閉原則

        The Liskov Substitution Principle (LSP)

        Liskov替換原則

        The Interface Segregation Principle (ISP)

        界面分離原則

        The Dependency inversion Principle (DIP)

        從屬倒置原則

        這五個原則并沒有一個特定的先后順序(做這個,然后做那個,等等),他們是歷經幾十年發(fā)展而成的最佳組合。為了方便記憶,他們被用一個縮略詞(SOLID)所概括。類似的方法在計算機中也有出現(xiàn)過,例如: DRY: Don 't Repeat Yourself;KISS: Keep It Small and Simple;每一個首字母背后都是人們智慧的結晶。順便提一下,這個縮寫詞是在這五項原則建立多年后才產生的。

        一般來說,SOLID原則是每個代碼開發(fā)人員的基本學習步驟,但通常它會被那些不把代碼質量放在第一位的人所忽略。

        然而,作為一名數(shù)據科學家,我認為遵循這些原則是有益的。具體地說,它提高了代碼的可測試性,減少了技術上的障礙和為了實現(xiàn)客戶/股東的新需求而修改代碼所需的時間。

        在下文里,我將探討這五個原則,并提供一些Python的示例。通常,SOLID原則應用于面向對象的編程情景中(即:Python的類),但我相信無論您的寫碼水平如何,他們都對您是有效的。我會在這里提供示例和解釋,面向的是“高級初學者”的級別。

        1) 單一任務原則(SRP)

        “一個類應該有且只有一個可以被改變的理由”

        換言之,代碼的每個部分((通常是一個類,但也可以是一個函數(shù)))應該有且只有一個職責。因此,應該只能有一個理由來改變它。

        您經常會看到一段代碼在處理整個進程。即,在那些返回結果之前加載數(shù)據、修改數(shù)據并繪制數(shù)據圖的函數(shù)。

        舉一個簡單的例子,我們有一串數(shù)組L = [n1, n2,…,nx],然后我們將對這個數(shù)組進行一些數(shù)學運算。例如,計算平均值、中位數(shù)等。

        比較糟糕的方法是讓一個函數(shù)做所有的工作:

        2.png

        為了使這個函數(shù)更符合SRP原則,我們應該做的第一件事是將函數(shù)math_operations分解為只有單一功能的函數(shù)!這樣的話,這個函數(shù)的職責就不能再往下繼續(xù)去細分了。

        第二步是創(chuàng)建一個函數(shù)(或類),一般命名為“main”。然后通過這個函數(shù)一步一步地調用所有其他函數(shù)。

        3.png

        現(xiàn)在,您就只有一個更改與“main”連接的函數(shù)的理由了。

        這樣做的結果是:

        1. 錯誤范圍縮小起來會更容易。進程中的任何錯誤都更具有指向性,從而加速了調試進程。

        2. 代碼的任何部分都可以在其他地方再次使用。

        3. 此外,經常被忽視的一點是,這樣做使得函數(shù)測試起來更加容易。關于測試的附注:您應該在實際編寫腳本之前就編寫好了測試。但是,為了創(chuàng)造一些好的結果并展示給利益相關者,這常常會被忽視。

        對于第一個示例來說,這已經是一個很大的改進了。但是,創(chuàng)建一個“main”函數(shù)和調用只有單一責任的函數(shù)并沒有實現(xiàn)SR的全部原則。

        事實上,我們的“main”有許多原因需要去改變。這個類實際上是很脆弱的,很難去維護。

        為了解決這個問題,讓我們介紹下一個原則:

        2) 開閉原則 (OCP

        “軟件實體 … 應該對拓展升級開放,對調試修改封閉”

        也就是說:您不需要修改已經編寫好的代碼以適應新的需求,而只需添加您現(xiàn)在需要的東西。

        這并不是說,當代碼的前提需要修改時,您不用更改代碼,而是說,如果您需要添加與現(xiàn)有函數(shù)類似的新函數(shù),您不應當更改代碼的其他部分。

        為了澄清這一點,讓我們參考前面看到的示例。如果我們想添加新的功能,例如,計算中位數(shù),我們應該創(chuàng)建一個新的函數(shù),并將其調用添加到“main”中。這將增加一個擴展函數(shù),同時也修改了“main”。

        我們可以通過將我們編寫的所有函數(shù)轉換成一個類的子類的方法來解決這個問題。在本例中,我創(chuàng)建了一個名為“Operations”的抽象類和一個抽象方法“get_operation”。(抽象類這個知識點通常要求比較高。如果您不知道什么是抽象類,您可以先運行以下代碼進行嘗試)。

        現(xiàn)在,所有舊的函數(shù)和類都被__subclasses__()方法調用。它將找到所有從Operations繼承的類,并運行存在于所有子類中的函數(shù)“Operations”。

        4.png

        如果現(xiàn)在我們想要添加一個新的功能,例如:median,我們只需要添加一個從類“Operations”繼承的類“median”。新形成的子類將立即被__subclasses__()獲取,不需要對代碼的其他部分進行修改。

        我們將會得到一個非常靈活的類,它所需的維護時間也會是最少的。

        3) Liskov 替換原則(LSP)

        “那些使用指針或者引用基類的函數(shù)必須要在不知情的情況下使用派生類的對象”

        也可以說,“派生類對于他們的基類來說必須要是可替換的才行”.

        簡單地說,如果子類重新定義了同樣在父類中出現(xiàn)的函數(shù),用戶不應該注意到任何差異,它只是基類的一個替代品。

        例如,如果您正在使用一個函數(shù),而您的同事更改了基類,那您不應該注意到任何差異。

        在所有的SOLID原則中,這是最難理解和解釋的。對于這個原則,沒有標準的“教科書式的”解決方案,而且很難提供一個“標準示例”來展示。

        我可以用最簡單的方式來概括這一原則:

        如果在子類中重新定義了基類中也存在的函數(shù),那么這兩個函數(shù)應該具有相同的行為。但是,這并不意味著它們必須強制性等同,而是:給定相同輸入能得出相同類型的結果。

        在示例ocp.py中,“operation”方法出現(xiàn)在子類和基類中,終端用戶應該期望從這兩個類中得到相同的行為。

        這一原則的結果是,我們將以一致的方式編寫代碼,只有終端用戶會需要去了解我們的代碼是如何工作的。

        附錄:

        (您可以跳到下一個原則)。

        LSP的一個結果是: 在子類中重新定義的新函數(shù)應該是有效的,并且可能在父類中使用相同的函數(shù)時被調用。

        這不是我們所常見的情況,事實上,通常我們人類,用集合論的方法來思考。我們的類會首先定義概念,然后用子類去擴展之前定義的類的概念及其不同行為。

        例如,超類“哺乳動物”的子類 “鴨嘴獸”就是一個例外,那就是這些哺乳動物會產卵。LSP原則告訴我們它將創(chuàng)建一個名為“give_birth”的函數(shù),這個函數(shù)將對子類Platypus和子類Dog有不同的行為。因此,我們應該會有一個比哺乳類更抽象的基類來適應這一點。

        如果這聽起來非常令人困惑,請不要擔心,LSP原則在這方面的應用很少有被實現(xiàn),目前還停留在理論階段。

        4) 界面分離原則 (ISP

        “具有很多面向特定客戶的界面會比只有一個的通用的界面要好”

        在寫類的時候,我們一般都只考慮使用一個界面,所有的方法和屬性都“暴露”出來了,因此,所有用戶可以與之交互的東西都屬于這個類。

        在這種意義上,IS原則告訴我們,類其實有所需的界面(SRP)就行了,我們應該避免那些無法工作或沒有理由成為該類一部分的方法。

        當子類從它不需要的基類繼承方法時,就會出現(xiàn)這個問題。

        讓我們來看一個例子:

        5.png

        對于這個例子,我們有一個抽象類 “Mammal”,它有兩個抽象方法:“walk”和“swim”。這兩個元素將屬于“Human”子類,而只有“swim”將屬于“Whale”子類。

        實際上,如果我們運行這段代碼,我們會得到:

        6.png

        子類whale仍然可以調用方法“walk”,但它不應該這樣做,我們必須避免它。

        ISP建議的方法是創(chuàng)建更多面向特定用戶的界面,而不是一個通用的界面。因此,我們的代碼示例變成如下:

        7.png

        現(xiàn)在,每個子類只繼承它需要的東西,避免了調用斷章取義(錯誤)的子方法。如果繼承了不需要的東西,可能會產生難以捕捉的錯誤。

        這一原則與其他原則緊密相連,具體來說,它告訴我們要保證子類的內容整潔度,排除對子類沒有用處的元素。這樣做的最終目的是讓我們的類能夠保持整潔,并將錯誤最小化。

        5) 從屬倒置原則(DIP)

        “抽象類不應該依賴于細節(jié)。細節(jié)應該依賴于抽象類。高級模塊不應該依賴于低級模塊。兩者都應該依賴于抽象類”

        因此,抽象類(例如,上面看到的界面)不應該依賴于低級方法,而應該都依賴于第三個界面。

        為了更好地解釋這個概念,我傾向于認為這是一種信息流。

        假設您有一個程序,它接收一組特定的信息(文件、格式等),然后您編寫了一個腳本來處理它。

        如果這些信息有變化會發(fā)生什么?

        你將不得不重寫你的腳本并調整新的格式。失去與舊文件的兼容性。

        然而,您可以通過創(chuàng)建第三個抽象類來解決這個問題,該抽象類將信息作為輸入并將其傳遞給其他抽象類。

        這基本上也是API的用途。

        8.png

        這一原則的設計理念有趣在于,它與我們通常的做法相反。

        考慮到DIP原則,我們將從項目的尾部開始,我們的代碼獨立于所輸入的內容,它不受更改的影響,并且不受我們的直接控制。

        我希望您能在您的代碼中運用這些概念,我知道它們是為我準備的。除此以外,我還提供了一些我用來理解這些原則的材料。

        R. C. Martin, “The Principles of OOD,” 2013. 

        http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

        https://codingwithjohan.com/blog/solid-python-introduction

        “Clean Code in Python” by Mariano Anaya

        原文標題:

        SOLID Coding in Python

        原文鏈接:

        https://towardsdatascience.com/solid-coding-in-python-1281392a6a94

        *博客內容為網友個人發(fā)布,僅代表博主個人觀點,如有侵權請聯(lián)系工作人員刪除。



        關鍵詞: AI

        相關推薦

        技術專區(qū)

        關閉
        主站蜘蛛池模板: 桃江县| 宜君县| 积石山| 浪卡子县| 石首市| 英超| 拉萨市| 皋兰县| 晴隆县| 喀喇沁旗| 如东县| 确山县| 黄山市| 菏泽市| 淮南市| 全椒县| 修水县| 云和县| 濉溪县| 新建县| 桃园县| 开远市| 沾化县| 突泉县| 四川省| 九江县| 米脂县| 延吉市| 讷河市| 新蔡县| 汾西县| 顺昌县| 岫岩| 海伦市| 兴文县| 噶尔县| 靖远县| 祁连县| 澄城县| 禄劝| 洛浦县|