對于代碼整潔,沒(méi)有唯一的或者嚴格的定義,而且可能無(wú)法正式地衡量怎樣才算代碼整潔,因此你不能在代碼倉庫上運行一個(gè)可以告訴你代碼是好是壞、可維護性如何的工具。當然,你可以運行檢查器、代碼校驗器、靜態(tài)分析器等工具。這些工具會(huì )給你很大的幫助。它們是必需的,但光有這些還遠遠不夠。代碼整潔與否不是機器或腳本能說(shuō)了算的(到目前為止),而是作為專(zhuān)業(yè)人員的我們才能決定的。
幾十年來(lái),我們沿用“編程語(yǔ)言”這個(gè)術(shù)語(yǔ),并將其視為把我們的想法傳達給計算機的語(yǔ)言,可以讓計算機運行我們的程序。但是我們錯了,這僅僅是部分事實(shí)。編程語(yǔ)言背后的“真正語(yǔ)言”是將我們的想法傳達給其他開(kāi)發(fā)人員的語(yǔ)言。
這才是代碼整潔的真正本質(zhì)所在。它取決于其他開(kāi)發(fā)人員是否能夠讀取和維護代碼。作為專(zhuān)業(yè)人士,我們是唯一能夠判斷這一點(diǎn)的人。想想看,作為開(kāi)發(fā)人員,我們閱讀代碼的時(shí)間比實(shí)際編寫(xiě)代碼的時(shí)間要多得多。每當我們想要更改或添加新功能,首先必須閱讀需要修改或擴展的代碼的所有上下文內容。編程語(yǔ)言(Python)就是開(kāi)發(fā)人員實(shí)現互相溝通的語(yǔ)言。
為什么保持代碼整潔如此重要,原因有很多。大多數原因與可維護性、減少技術(shù)債務(wù)、有效配合敏捷開(kāi)發(fā)以及管理一個(gè)成功的項目的想法有關(guān)。
我們想探討的第一個(gè)想法是關(guān)于敏捷開(kāi)發(fā)和持續交付的。如果希望項目能夠以穩定和可預測的速度不斷成功地交付特性,那么必須有一個(gè)良好且可維護的代碼庫。
假設你正駕駛著(zhù)一輛汽車(chē)行駛在去往某個(gè)目的地的道路上,而且想要在某個(gè)時(shí)間點(diǎn)之前到達那里。你必須預估自己到達目的地的時(shí)間,這樣才能告知正在等你的人。如果汽車(chē)不出故障,道路十分平坦,那么你的預估不大可能有太大的偏差;相反,如果道路被破壞,你必須下車(chē)把石頭移開(kāi),或者要避開(kāi)裂縫,抑或每隔幾千米就必須停下來(lái)檢查一下發(fā)動(dòng)機等,那么你不太可能確定什么時(shí)候到達(或者你是否能到達)。這個(gè)比喻明確易懂,這里的道路可以理解為代碼。如果希望以穩定、恒定和可預測的速度向前推進(jìn)項目,那么代碼應該是可維護和可讀的。
技術(shù)債務(wù)是指因妥協(xié)或所做出的不良決策而導致的軟件問(wèn)題。在某種程度上,我們可以從兩個(gè)方面考慮技術(shù)債務(wù)問(wèn)題。一是從過(guò)去到現在,如果我們當前面臨的問(wèn)題是由之前編寫(xiě)的錯誤代碼造成的,那會(huì )怎樣?二是從現在到將來(lái),如果我們決定現在就走捷徑,而不是花時(shí)間去尋找合適的解決方案,那么未來(lái)又會(huì )為自己帶來(lái)什么麻煩?
“債務(wù)”這個(gè)詞用得恰如其分。這是一筆債務(wù),因為在未來(lái)代碼將比現在更難以修改。產(chǎn)生的成本就是債務(wù)的利息。產(chǎn)生的技術(shù)債務(wù)意味著(zhù),明天修改代碼比今天更困難,成本更高,而且后天的成本會(huì )更昂貴,等等。
一旦團隊不能按時(shí)交付一些東西,并且不得不停下來(lái)去修復和重構代碼,代碼就要付出技術(shù)債務(wù)的代價(jià)。
技術(shù)債務(wù)最糟糕的一點(diǎn)是它代表了一個(gè)長(cháng)期和根本的問(wèn)題。這不是什么值得高度警覺(jué)的東西。相反,這是一個(gè)悄無(wú)聲息的問(wèn)題,這個(gè)問(wèn)題分散在整個(gè)項目的各個(gè)部分,在某一天,在某一個(gè)特定的時(shí)間,這個(gè)問(wèn)題會(huì )“醒來(lái)”,并成為項目推進(jìn)的阻礙。
代碼整潔是指根據一些標準(例如,PEP-8或由項目規范定義的自定義標準)進(jìn)行的代碼格式化和結構化嗎?并非如此。
代碼整潔遠遠不止編碼標準、格式化、美化工具和其他有關(guān)代碼布局的檢查這些內容。代碼整潔是關(guān)于實(shí)現高質(zhì)量的軟件和建立一個(gè)健壯、可維護和避免技術(shù)債務(wù)的系統的。一段代碼或整個(gè)軟件組件可以百分之百符合PEP-8(或任何其他準則)標準,但仍可能無(wú)法滿(mǎn)足上述要求。
然而,不注意代碼的結構會(huì )有一些危險。鑒于此,我們先來(lái)分析不良代碼結構的問(wèn)題以及如何解決這些問(wèn)題,然后介紹如何為Python項目配置和使用工具,以便自動(dòng)檢查和糾正問(wèn)題。
綜上所述,我們可以說(shuō)代碼整潔與PEP-8或編碼風(fēng)格沒(méi)有任何關(guān)系。代碼整潔的意義遠不止于此,除了可維護性和軟件的質(zhì)量,它還意味著(zhù)更有意義的東西。不過(guò),正如你將看到的,要實(shí)現高效工作,正確地格式化代碼非常重要。
編碼準則是項目在質(zhì)量標準下開(kāi)發(fā)時(shí)必須考慮的最低要求。在本節中,我們將探討其背后的原因。接下來(lái)我們開(kāi)始探討如何通過(guò)工具自動(dòng)在項目中遵循編碼風(fēng)格準則。
在試圖考慮在代碼布局中找到某種好的特性時(shí),我們首先想到的就是一致性。我們希望代碼能夠具有一致的結構,以便更易閱讀和理解。如果代碼不正確或者結構不一致,并且團隊中的每個(gè)人都以自己的方式做事,那么最終得到的將是需要額外努力和集中精力才能正確理解的代碼。這樣的代碼很容易引起誤解,并且由此引發(fā)的漏洞或微小的錯誤很可能被忽略。
上述情況是我們想要避免的。我們想要的是一眼就能讀懂和理解的代碼。
如果開(kāi)發(fā)團隊的成員都同意采用標準化的方式編寫(xiě)代碼,那么所得到的代碼看起來(lái)會(huì )更加熟悉。這樣,你就能快速識別模式,并且記住這些模式,進(jìn)而能更容易地理解內容和檢測錯誤。例如,當某些代碼出錯時(shí),你可能會(huì )在你熟悉的模式中看到一些奇怪的東西——它們會(huì )吸引你的注意,再仔細觀(guān)察,就很可能發(fā)現錯誤!
正如經(jīng)典著(zhù)作Code Complete中所述的,在名為Perception in Chess(1973年)的論文中對此進(jìn)行了有趣的分析,該論文提到了一項實(shí)驗,以確定不同的人如何理解或記憶不同的棋局。該實(shí)驗針對不同級別的棋手(新手、中級棋手和象棋高手)以及棋盤(pán)上不同位置的棋局來(lái)進(jìn)行統計。他們發(fā)現,當棋子的位置是隨機的時(shí)候,新手能和象棋高手表現得一樣好。因為這只是一個(gè)記憶練習,任何人都可以發(fā)揮出合理的水平。但當棋子的位置遵循一個(gè)可能發(fā)生在一場(chǎng)真正對弈中的一些邏輯順序(或者,遵守某種一致性,堅持某種模式時(shí))時(shí),那么象棋高手們的表現比其他人要好得多了。
現在我們想象一下,同樣的情況也適用于軟件開(kāi)發(fā)。作為Python方面的軟件工程師專(zhuān)家,我們就好比上述例子中的象棋高手。如果代碼的結構是隨機的,沒(méi)有遵循任何邏輯或者沒(méi)有遵循任何標準,我們就會(huì )像一個(gè)新手開(kāi)發(fā)人員一樣,很難發(fā)現錯誤;如果我們習慣以結構化的方式閱讀代碼,并且通過(guò)遵循這種模式學(xué)會(huì )從代碼中快速獲得想法,就會(huì )在項目開(kāi)發(fā)中比其他開(kāi)發(fā)人員更有優(yōu)勢。
就Python而言,你應該遵循的編碼風(fēng)格是PEP-8。你可以對其進(jìn)行擴展或采用其中的一部分,以適應正在參與的項目的某種特殊性(如行的長(cháng)度、字符串的注釋等)。不過(guò),我們建議,無(wú)論你使用最原始版本的PEP-8規范還是對它進(jìn)行擴展,都應該堅持使用,而不是從頭開(kāi)始嘗試另一個(gè)不同的標準。
這是因為PEP-8充分考慮了Python語(yǔ)法的許多特殊性(通常不適用于其他語(yǔ)言),并且它是由對Python語(yǔ)法做出貢獻的核心Python開(kāi)發(fā)人員創(chuàng )建的。因此,我們認為其他標準其實(shí)很難與PEP-8相提并論,更不用說(shuō)超越它了。
尤其是,在處理代碼時(shí),PEP-8還有一些不錯的改進(jìn)特性。
(1)可進(jìn)行g(shù)rep。這就是在代碼中對內容進(jìn)行g(shù)rep的能力,即在某些文件(以及這些文件的某個(gè)部分)中搜索所要查找的特定字符串。PEP-8引入的特性之一是區分將值賦值寫(xiě)入變量的方式和傳遞給函數的關(guān)鍵字參數的方式。
為了更好地理解這一點(diǎn),我們用一個(gè)示例加以闡釋。假設我們正在進(jìn)行調試,需要找到名為location的參數值的傳遞位置。我們可以運行以下grep命令,獲悉要查找內容所在的文件和行號。
$ grep -nr 'location=' .
./core.py:13: location=current_location,現在,我們想知道這個(gè)變量在哪里被分配這個(gè)值,則可以運行以下命令。
$ grep -nr 'location =' .
./core.py:10: current_location = get_location()PEP-8建立了這樣一種約定,即當通過(guò)關(guān)鍵字向函數傳遞參數時(shí),不使用空格,但在分配變量時(shí)使用空格。因此,我們可以調整搜索條件(第一次搜索時(shí)等號兩側沒(méi)有空格,第二次搜索時(shí)等號兩側都有一個(gè)空格),從而提高搜索效率。這是遵守約定的好處之一。
(2)一致性。如果代碼看起來(lái)有一種統一的格式,閱讀起來(lái)就會(huì )容易得多。這對于新加入項目的人來(lái)說(shuō)尤為重要,如果你希望有新的開(kāi)發(fā)人員加入項目,或者為團隊聘用新的(可能經(jīng)驗不足的)程序員,那么他們勢必要熟悉代碼(甚至可能由多個(gè)代碼倉庫組成)。如果代碼格式、文檔、命名約定等在所有代碼倉庫的所有文件中都是相同的,那么他們的工作將變得更加輕松。
(3)代碼質(zhì)量。以結構化的方式查看代碼,你一下子就能更熟練地理解它(就像在Perception in Chess中所說(shuō)的那樣),并且更容易發(fā)現程序的漏洞和錯誤。除此之外,檢查代碼質(zhì)量的工具也會(huì )提示潛在的錯誤。對代碼的靜態(tài)分析可能有助于降低每行代碼的錯誤率。
本文摘自《編寫(xiě)整潔的Python代碼》
這是一本關(guān)于Python軟件工程原理方面的書(shū)。
關(guān)于軟件工程的書(shū)有很多,關(guān)于Python的可用資源也有很多,但要將這兩者結合起來(lái),還有許多工作要做。本書(shū)正是嘗試在這二者之間架起一座橋梁。
要想在一本書(shū)中涵蓋關(guān)于軟件工程的所有主題是不現實(shí)的,因為軟件工程的領(lǐng)域十分廣泛,而且針對某個(gè)特定的主題會(huì )有專(zhuān)門(mén)的圖書(shū)去介紹。本書(shū)重點(diǎn)介紹Python軟件工程的主要實(shí)踐和原則,旨在幫助讀者編寫(xiě)更易于維護的代碼,同時(shí)教讀者利用Python的特性來(lái)編寫(xiě)代碼。
第1章 簡(jiǎn)介、代碼格式和工具,介紹搭建Python開(kāi)發(fā)環(huán)境所需的主要工具、Python開(kāi)發(fā)人員在開(kāi)始使用該語(yǔ)言時(shí)需要了解的基本知識,以及維護項目中代碼可讀性的一些指導原則,如用于靜態(tài)分析、文檔、類(lèi)型檢查和代碼格式化的工具。
第2章 Python風(fēng)格代碼,介紹Python中的第一個(gè)習慣用法——我們在后續章節中將繼續使用它。本章還會(huì )介紹一些Python的獨有特性,以及如何使用它們,并且開(kāi)始圍繞“Python風(fēng)格代碼如何能夠讓代碼質(zhì)量更高”展開(kāi)論述。
第3章 好代碼的一般特征,回顧軟件工程的一般原則,以期幫助讀者編寫(xiě)可維護的代碼。本章就這個(gè)話(huà)題展開(kāi)討論,并利用Python語(yǔ)言中的工具應用這些原則。
第4章 SOLID原則,介紹面向對象軟件設計的SOLID原則。SOLID是軟件工程領(lǐng)域的行業(yè)術(shù)語(yǔ),即SRP、OCP、LSP、ISP和DIP。本章會(huì )展示這5項原則在Python中的應用??梢哉f(shuō),鑒于Python語(yǔ)言的性質(zhì),并非所有方法都完全適用。
第5章 用裝飾器改進(jìn)代碼,介紹Python的最大特性之一——裝飾器。在了解如何創(chuàng )建裝飾器(用于函數和類(lèi))之后,我們將其用于代碼重用、責任分離和創(chuàng )建更細粒度的函數。
第6章 用描述符從對象中獲取更多信息,探討Python中的描述符,它把面向對象設計提升到了一個(gè)新的層次。盡管這更多只是一個(gè)與框架和工具相關(guān)的特性,但我們可以看到如何用描述符提高代碼的可讀性,以及如何重用代碼。
第7章 使用生成器,說(shuō)明生成器可能是Python的最佳特性。事實(shí)上,迭代是Python的核心組件,這讓我們認為它引申出了一種新的編程范式。一般來(lái)說(shuō),通過(guò)使用生成器和迭代器,我們可以考慮編寫(xiě)程序的方式?;趶纳善髦形〉闹R,我們將進(jìn)一步了解Python中的協(xié)同程序以及異步編程的基本知識。
第8章 單元測試和重構,討論單元測試在任何所謂“可維護的代碼庫”中的重要性。本章回顧了單元測試的重要性,并探究了單元測試的主要框架(unittest和pytest)。
第9章 常見(jiàn)的設計模式,回顧如何在Python中實(shí)現最常見(jiàn)的設計模式,不是從解決問(wèn)題的角度,而是通過(guò)研究如何利用更好和更易于維護的解決方案來(lái)解決問(wèn)題。本章提到了Python的一些特性,這些特性使得一些設計模式不可見(jiàn),并采用實(shí)用的方法實(shí)現了其中的一些設計模式。
第10章 整潔架構,強調代碼整潔是實(shí)現良好架構的基礎。我們在第1章中提到的所有細節,以及在此過(guò)程中重溫的其他內容,在部署系統時(shí)都將在整個(gè)設計中發(fā)揮關(guān)鍵作用。
聯(lián)系客服