Python高效代碼實(shí)踐:性能、內(nèi)存和可用性
點(diǎn)擊上方Python知識(shí)圈,設(shè)為星標(biāo)
回復(fù)100獲取100題PDF
閱讀文本大概需要 5 分鐘
占用盡可能小的內(nèi)存、執(zhí)行地更快、看起來(lái)整潔、文檔正確、遵循標(biāo)準(zhǔn)風(fēng)格指南,并且易于被新開(kāi)發(fā)者理解。
1. 盡量不要對(duì)內(nèi)存置之不理
使用 format 而不是 “+” 來(lái)生成字符串 —— 在Python中,str 是不可變的,所以每對(duì)連接都必須將左、右字符串復(fù)制到新的字符串中。如果連接長(zhǎng)度為10的四個(gè)字符串,則將復(fù)制(10+10) + ((10+10)+10) + (((10+10)+10)+10) = 90 個(gè)字符,而不是 40 字符。隨著字符串?dāng)?shù)量和大小的增加,事情會(huì)變得越來(lái)越糟。Java 有時(shí)將一系列的連接轉(zhuǎn)換為使用StringBuilder 來(lái)優(yōu)化這種情況,但是 CPython 沒(méi)有。因此,建議使用 .format 或 % 語(yǔ)法。如果您不能在 .format 和 % 之間選擇,請(qǐng)查看 這個(gè)有趣的 StackOverflow 問(wèn)題[2] 。
定義 Python 類(lèi)時(shí)使用槽(slots)。您可以通過(guò)將類(lèi)中的
__slots__設(shè)置為固定的屬性名稱(chēng)列表,來(lái)告訴 Python 不要使用動(dòng)態(tài)字典,只為一組固定的屬性分配空間,從而消除了為每個(gè)對(duì)象使用一個(gè)字典的開(kāi)銷(xiāo)。在 這里[3] 閱讀更多關(guān)于槽的內(nèi)容。您可以通過(guò)使用內(nèi)置的模塊(如 resource 和 objgraph)來(lái)跟蹤對(duì)象級(jí)別的內(nèi)存使用情況。
在 Python 中管理內(nèi)存泄漏可能是一項(xiàng)艱巨的任務(wù),但幸運(yùn)的是有一些工具(如 heapy)用于調(diào)試內(nèi)存泄漏。Heapy 可以與 objgraph 一起使用來(lái)觀察 diff 對(duì)象的分配隨時(shí)間而增長(zhǎng)。Heapy 可以顯示哪些對(duì)象占用最多的內(nèi)存。Objgraph 可以幫助您找到反向引用,以明白為什么它們不能被釋放。您可以在 這里[4] 閱讀更多關(guān)于在Python中診斷內(nèi)存泄漏的信息。
2. Python2 還是 Python3
3. 寫(xiě)出美麗的代碼
PEP-8 風(fēng)格指南
Python 習(xí)語(yǔ)和效率
Google Python 風(fēng)格指南
使用靜態(tài)代碼分析工具
正確地文檔說(shuō)明代碼
該功能的一行概要。
如果適用的話,提供交互示例。這些可以讓新開(kāi)發(fā)人員參考,以快速了解功能的使用和預(yù)期的輸出。您也可以使用 doctest 模塊來(lái)確保這些示例的正確性(以測(cè)試方式運(yùn)行)。請(qǐng)參閱 doctest 文檔 中的示例。
參數(shù)文檔(通常一行描述參數(shù)及其在函數(shù)中的作用)
返回類(lèi)型的文檔(除非您的函數(shù)沒(méi)有返回任何內(nèi)容!)
4. 提高性能
多進(jìn)程,而不是多線程
操作系統(tǒng)(通過(guò)多進(jìn)程)
一些調(diào)用您的 Python 代碼的外部應(yīng)用程序(例如 Spark 或 Hadoop)
您的Python代碼所調(diào)用的代碼(例如,您可以讓 Python 代碼調(diào)用C函數(shù),來(lái)執(zhí)行昂貴的多線程內(nèi)容)。
使用最新版本的 Python:
這是最直接的方法,因?yàn)樾碌母峦ǔ0▽?duì)已經(jīng)存在功能性能方面的增強(qiáng)。盡可能使用內(nèi)置函數(shù):
這也符合 DRY 原則 —— 內(nèi)置函數(shù)由世界上一些最好的 Python 開(kāi)發(fā)人員仔細(xì)設(shè)計(jì)和審查,所以它們通常是最好的方式。考慮使用 Ctypes:
Ctypes 提供了一個(gè)在 Python 代碼中調(diào)用 C 共享函數(shù)的接口。C 是一種更接近機(jī)器級(jí)別的語(yǔ)言,與 Python 中的類(lèi)似實(shí)現(xiàn)相比,代碼執(zhí)行速度更快。使用 Cython:
Cython 是一種 Python 語(yǔ)言的超集,允許用戶調(diào)用 C 函數(shù)并具有靜態(tài)類(lèi)型聲明,最后生成一份更簡(jiǎn)單的最終代碼,可能會(huì)執(zhí)行得快得多。使用 PyPy:
PyPy 是具有 JIT(即時(shí))編譯器的另一個(gè) Python 實(shí)現(xiàn),可以使您的代碼執(zhí)行更快。雖然我從未嘗試過(guò) PyPy,但它也聲稱(chēng)會(huì)減少程序的內(nèi)存消耗。像 Quora 這樣的公司實(shí)際上在生產(chǎn)環(huán)境中使用 PyPy。設(shè)計(jì)與數(shù)據(jù)結(jié)構(gòu):
適用于各種語(yǔ)言。確保您正在為目標(biāo)使用正確的數(shù)據(jù)結(jié)構(gòu),在正確的地方聲明變量,明智地利用標(biāo)識(shí)符范圍,并在任何有意義的地方緩存結(jié)果等。
5. 分析您的代碼

memory_profiler 是一個(gè)用于監(jiān)視進(jìn)程內(nèi)存消耗的Python模塊,它能對(duì) Python 程序的內(nèi)存消耗進(jìn)行逐行分析。
objgraph 能顯示前N個(gè)占用 Python 程序內(nèi)存的對(duì)象、在一段時(shí)間內(nèi)刪除或添加的對(duì)象以及腳本中給定對(duì)象的所有引用。
resource 為程序測(cè)量和控制系統(tǒng)資源使用提供了基本機(jī)制。該模塊的兩個(gè)主要用途包括限制資源分配和獲取有關(guān)資源當(dāng)前使用情況的信息。
6. 測(cè)試和持續(xù)集成
測(cè)試
nose —— 可以運(yùn)行 unittest 測(cè)試,并具有較少的樣板。
pytest —— 也運(yùn)行unittest測(cè)試,更少的樣板,更好的報(bào)告和很多很酷,額外的功能。
測(cè)量覆蓋度
持續(xù)集成
在現(xiàn)實(shí)環(huán)境中運(yùn)行測(cè)試。有些情況下,測(cè)試在某些架構(gòu)上通過(guò),而在其他架構(gòu)上失敗。CI 服務(wù)可以讓您在不同的系統(tǒng)架構(gòu)上運(yùn)行測(cè)試。
對(duì)您的代碼庫(kù)執(zhí)行覆蓋度約束。
構(gòu)建和部署您的代碼到生產(chǎn)環(huán)境(您可以在不同的平臺(tái)上這樣做)
References
[1] https://jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/[2] https://stackoverflow.com/questions/5082452/string-formatting-vs-format[3] https://stackoverflow.com/questions/472000/usage-of-slots[4] https://chase-seibert.github.io/blog/2013/08/03/diagnosing-memory-leaks-python.html[5] http://deeplearning.net/software/theano/tutorial/python-memory-management.html[6] http://python-future.org/compatible_idioms.html[7] https://www.youtube.com/watch?v=KOqk8j11aAI&t=10m14s[8] https://docs.python-guide.org/writing/documentation/[9] https://pythonguidecn.readthedocs.io/zh/latest/[10] https://stackoverflow.com/questions/67299/is-unit-testing-worth-the-effort[11] https://pythontesting.net/start-here/往期推薦 01 02 03
↓點(diǎn)擊閱讀原文查看pk哥原創(chuàng)視頻
我就知道你“在看” 

