<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Python高效代碼實踐:性能、內(nèi)存和可用性

          共 6075字,需瀏覽 13分鐘

           ·

          2021-02-08 17:24

          譯者:Prodesire ?|??來自《編程小記》
          原文:https://dwz.cn/r4N2hvht譯文:https://zhuanlan.zhihu.com/p/28675694


          遵循最佳做法的代碼庫在當今世界能得到高度評價。如果您的項目是開源的,這會是一個吸引優(yōu)秀開發(fā)人員的方式。作為開發(fā)人員,您想要編寫高效且優(yōu)化的代碼:
          占用盡可能小的內(nèi)存、執(zhí)行地更快、看起來整潔、文檔正確、遵循標準風格指南,并且易于被新開發(fā)者理解。
          這里討論的實踐可能有助于您為開源組織做出貢獻,向在線評審(Online Judge)提交解決方案,使用機器學(xué)習(xí)處理大量數(shù)據(jù)處理問題,或開發(fā)自己的項目。

          1. 盡量不要對內(nèi)存置之不理

          一個簡單的 Python 程序在內(nèi)存上可能不會引起很多問題,但在高內(nèi)存消耗的項目中內(nèi)存使用變得至關(guān)重要。從一開始開發(fā)大項目時,合理使用內(nèi)存是明智的。與 C/C ++ 不同,Python 解釋器會進行內(nèi)存管理,用戶無法自己控制。Python 中的內(nèi)存管理涉及包含所有Python對象和數(shù)據(jù)結(jié)構(gòu)的專用堆。Python 內(nèi)存管理器內(nèi)部確保對這個專用堆的管理。當您創(chuàng)建對象時,Python 虛擬機處理所需的內(nèi)存,并決定將其放置在內(nèi)存布局中的特定位置。然而,如何更好地了解事情的工作原理和不同的方法來做事情,可以幫助您最大限度地減少程序的內(nèi)存使用量。使用生成器來計算大量的結(jié)果生成器可進行惰性計算。您可以通過遍歷來使用它們:顯示地使用 “for” 或者隱式地將其傳遞給任何方法或構(gòu)造。生成器可以返回多個項,就像返回一個列表 —— 不是一次返回所有,而是一個接一個地返回。生成器會暫停,直到下一個項被請求。在 這里[1] 閱讀更多關(guān)于 Python 生成器的內(nèi)容。對于大量數(shù)字/數(shù)據(jù)的處理,您可以使用像 Numpy 這樣的庫,它可以優(yōu)雅地處理內(nèi)存管理。
          • 使用 format 而不是 “+” 來生成字符串 —— 在Python中,str 是不可變的,所以每對連接都必須將左、右字符串復(fù)制到新的字符串中。如果連接長度為10的四個字符串,則將復(fù)制(10+10) + ((10+10)+10) + (((10+10)+10)+10) = 90 個字符,而不是 40 字符。隨著字符串數(shù)量和大小的增加,事情會變得越來越糟。Java 有時將一系列的連接轉(zhuǎn)換為使用StringBuilder 來優(yōu)化這種情況,但是 CPython 沒有。因此,建議使用 .format 或 % 語法。如果您不能在 .format 和 % 之間選擇,請查看 這個有趣的 StackOverflow 問題[2] 。

          • 定義 Python 類時使用槽(slots)。您可以通過將類中的 __slots__ 設(shè)置為固定的屬性名稱列表,來告訴 Python 不要使用動態(tài)字典,只為一組固定的屬性分配空間,從而消除了為每個對象使用一個字典的開銷。在 這里[3] 閱讀更多關(guān)于槽的內(nèi)容。

          • 您可以通過使用內(nèi)置的模塊(如 resource 和 objgraph)來跟蹤對象級別的內(nèi)存使用情況。

          • 在 Python 中管理內(nèi)存泄漏可能是一項艱巨的任務(wù),但幸運的是有一些工具(如 heapy)用于調(diào)試內(nèi)存泄漏。Heapy 可以與 objgraph 一起使用來觀察 diff 對象的分配隨時間而增長。Heapy 可以顯示哪些對象占用最多的內(nèi)存。Objgraph 可以幫助您找到反向引用,以明白為什么它們不能被釋放。您可以在 這里[4] 閱讀更多關(guān)于在Python中診斷內(nèi)存泄漏的信息。

          您可以在 這里[5] 閱讀由 Theano 的開發(fā)人員編寫的關(guān)于 Python 內(nèi)存管理的細節(jié)。

          2. Python2 還是 Python3

          當開始一個新的 Python 項目,或是只學(xué)習(xí) Python,您可能會發(fā)現(xiàn)自己在選擇 Python2 還是Python3 上十分糾結(jié)。這是一個廣泛討論的話題,在網(wǎng)上有許多觀點和好的解釋。一方面,Python3 有一些很棒的新特性。另一方面,您可能希望使用僅支持 Python2 的包,而Python3 不能向后兼容。這意味著在 Python3.x 的解釋器上運行 Python2 的代碼可能會拋出錯誤。不過,編寫能同時跑在 Python2 和 Python3 解釋器的代碼是可能的。最常見的方法是使用_future、builtins 和 six 這樣的軟件包來維護一個簡單、干凈的 Python3.x 兼容代碼庫,能以最小的開銷同時支持Python2 和 Python3。python-future 是 Python2 和 Python3 之間的缺失兼容層。它提供 future 和 past 的包,能夠向前或向后移植 Python2 和 Python3 的特性。它還帶有 futurize 和 pasteurize,定制化的 2 到 3 基礎(chǔ)的腳本,可以幫助您輕松地將 Py2 或 Py3 代碼逐模塊轉(zhuǎn)換為干凈的支持 Python2 和 Python3 的Py3 風格的代碼庫。請查看 Ed Schofield 編寫的超贊的 Python 2-3 兼容代碼 手抄冊[6](需翻墻)。如果相比閱讀,您更喜歡視頻,可以在 PyCon AU 2014 上找到他的演講,“編寫 2/3 兼容的代碼[7]”(需翻墻)。

          3. 寫出美麗的代碼

          分享代碼是一個有益的嘗試。無論什么動機,如果人們發(fā)現(xiàn)您的代碼難以使用或理解,那么您的良好意圖可能沒有達到預(yù)期。幾乎每個組織都遵循開發(fā)人員必須遵循的風格指南,以保持一致性、易于調(diào)試和協(xié)作。Python 的禪就像一個迷你風格的 Python 設(shè)計指南。主流的 Python 風格指南包括:
          1. PEP-8 風格指南

          2. Python 習(xí)語和效率

          3. Google Python 風格指南

          這些準則討論了如何使用:空格、逗號和大括號,對象命名指南等。盡管它們在某些情況下可能發(fā)生沖突,但它們都具有相同的目標 —— “清晰、可讀和可調(diào)試的代碼標準”。堅持一個指南,或遵循自己的,但不要試圖跟隨與廣泛接受的標準大不相同的內(nèi)容。

          使用靜態(tài)代碼分析工具

          有許多可用的開源工具能夠使您的代碼符合標準的風格指南和編寫代碼的最佳實踐。Pylint 是一個 Python 工具,用于檢查模塊的編碼標準。Pylint 可以快速輕松地查看您的代碼是否捕捉到了 PEP-8 的精髓,因此對其他潛在用戶是“友好的”。它還為您提供優(yōu)良的指標和統(tǒng)計報告,可幫助您判斷代碼質(zhì)量。您還可以通過創(chuàng)建自己的 .pylintrc 文件進行自定義和使用。Pylint 不是唯一的選擇 —— 還有其他工具,如 PyChecker,PyFlakes 以及像 pep8 和 flakes8 這樣的包。我的建議是使用 coala,一個統(tǒng)一的靜態(tài)代碼分析框架,旨在通過單個框架提供語言非特定的代碼分析。Coala 支持我之前提到的所有的linting工具,并且是高度可定制的。

          正確地文檔說明代碼

          這方面對您的代碼庫的可用性和可讀性至關(guān)重要。始終建議您盡可能廣泛地文檔說明您的代碼,以便其他開發(fā)人員更容易了解您的代碼。功能的典型內(nèi)聯(lián)文檔應(yīng)包括:
          • 該功能的一行概要。

          • 如果適用的話,提供交互示例。這些可以讓新開發(fā)人員參考,以快速了解功能的使用和預(yù)期的輸出。您也可以使用 doctest 模塊來確保這些示例的正確性(以測試方式運行)。請參閱 doctest 文檔 中的示例。

          • 參數(shù)文檔(通常一行描述參數(shù)及其在函數(shù)中的作用)

          • 返回類型的文檔(除非您的函數(shù)沒有返回任何內(nèi)容!)

          Sphinx 是廣泛使用的用于生成和管理項目文檔的工具。它提供了大量方便的功能,可以減少您編寫標準文檔的工作量。此外,您可以將文檔推送到 Read the Docs,這是最常用的托管項目文檔的方式。Hitchiker's guide to Python for documentation[8] (筆者翻譯成了中文版——Python 最佳實踐指南[9])包含一些有趣的信息,在文檔說明代碼時可能對您有用。

          4. 提高性能

          多進程,而不是多線程

          改進多任務(wù)代碼的執(zhí)行時間時,您可能希望利用 CPU 中的多核同時執(zhí)行多個任務(wù)。產(chǎn)生幾個線程并讓它們并發(fā)執(zhí)行可能看起來很直觀,但是由于 Python 中的全局解釋器鎖,所有的線程都是在相同的核上輪流運行。為了實現(xiàn) Python 的實際并行化,您可能需要使用 Python 的 multiprocessing 模塊。另一個解決方案可以是將任務(wù)外包給:
          1. 操作系統(tǒng)(通過多進程)

          2. 一些調(diào)用您的 Python 代碼的外部應(yīng)用程序(例如 Spark 或 Hadoop)

          3. 您的Python代碼所調(diào)用的代碼(例如,您可以讓 Python 代碼調(diào)用C函數(shù),來執(zhí)行昂貴的多線程內(nèi)容)。

          除了并行,還有其他方法可以提高您的性能。其中一些包括:
          • 使用最新版本的 Python:
            這是最直接的方法,因為新的更新通常包括對已經(jīng)存在功能性能方面的增強。

          • 盡可能使用內(nèi)置函數(shù):
            這也符合 DRY 原則 —— 內(nèi)置函數(shù)由世界上一些最好的 Python 開發(fā)人員仔細設(shè)計和審查,所以它們通常是最好的方式。

          • 考慮使用 Ctypes:
            Ctypes 提供了一個在 Python 代碼中調(diào)用 C 共享函數(shù)的接口。C 是一種更接近機器級別的語言,與 Python 中的類似實現(xiàn)相比,代碼執(zhí)行速度更快。

          • 使用 Cython:
            Cython 是一種 Python 語言的超集,允許用戶調(diào)用 C 函數(shù)并具有靜態(tài)類型聲明,最后生成一份更簡單的最終代碼,可能會執(zhí)行得快得多。

          • 使用 PyPy:
            PyPy 是具有 JIT(即時)編譯器的另一個 Python 實現(xiàn),可以使您的代碼執(zhí)行更快。雖然我從未嘗試過 PyPy,但它也聲稱會減少程序的內(nèi)存消耗。像 Quora 這樣的公司實際上在生產(chǎn)環(huán)境中使用 PyPy。

          • 設(shè)計與數(shù)據(jù)結(jié)構(gòu):
            適用于各種語言。確保您正在為目標使用正確的數(shù)據(jù)結(jié)構(gòu),在正確的地方聲明變量,明智地利用標識符范圍,并在任何有意義的地方緩存結(jié)果等。

          我可以給出的一個具體的例子是:Python 通常在訪問全局變量和解析函數(shù)地址時很慢,所以將它們分配到當前作用域內(nèi)的一個局部變量,然后訪問它們,速度會更快。

          5. 分析您的代碼

          通常,分析您的代碼的覆蓋度、質(zhì)量和性能是有幫助的。Python 附帶了 cProfile 模塊來幫助評估性能。它不僅給出了總運行時間,還分別對每個函數(shù)進行了計時。然后,它會告訴您每個函數(shù)調(diào)用的時間,這樣可以很容易地確定要優(yōu)化的地方。以下是cProfile 的一個示例分析:14e8261e5a2980cbf65fcea674e3e97c.webpnull
          • memory_profiler 是一個用于監(jiān)視進程內(nèi)存消耗的Python模塊,它能對 Python 程序的內(nèi)存消耗進行逐行分析。

          • objgraph 能顯示前N個占用 Python 程序內(nèi)存的對象、在一段時間內(nèi)刪除或添加的對象以及腳本中給定對象的所有引用。

          • resource 為程序測量和控制系統(tǒng)資源使用提供了基本機制。該模塊的兩個主要用途包括限制資源分配和獲取有關(guān)資源當前使用情況的信息。

          6. 測試和持續(xù)集成

          測試

          寫單元測試是個好習(xí)慣。如果您認為寫測試不值得您努力,請查看此 StackOverflow 問題[10]。最好在編碼之前或期間編寫測試。Python 提供了unittest 模塊來為函數(shù)和類編寫單元測試。此外還有如下框架:
          • nose —— 可以運行 unittest 測試,并具有較少的樣板。

          • pytest —— 也運行unittest測試,更少的樣板,更好的報告和很多很酷,額外的功能。

          為了得到良好的比較,請閱讀這里[11]的介紹。不要忘記 doctest 模塊,它使用內(nèi)聯(lián)文檔中的交互式示例來測試源代碼。

          測量覆蓋度

          Coverage 是測量 Python 程序代碼覆蓋度的工具。它監(jiān)控您的程序,關(guān)注代碼的哪些部分已被執(zhí)行,然后分析源碼以識別可能已被執(zhí)行但沒有執(zhí)行的代碼。覆蓋度測量通常用于測量測試的有效性。它可以顯示您的代碼的哪些部分被測試執(zhí)行了,哪些沒有。通常建議有 100% 的分支覆蓋度,這意味著您的測試應(yīng)該能夠執(zhí)行和驗證項目的每個分支的輸出。

          持續(xù)集成

          從一開始就為您的項目配置 CI 系統(tǒng),對于您的項目來說可以非常有用。您可以使用 CI 服務(wù)輕松測試代碼庫的各個方面。CI 中的一些典型檢查包括:
          • 在現(xiàn)實環(huán)境中運行測試。有些情況下,測試在某些架構(gòu)上通過,而在其他架構(gòu)上失敗。CI 服務(wù)可以讓您在不同的系統(tǒng)架構(gòu)上運行測試。

          • 對您的代碼庫執(zhí)行覆蓋度約束。

          • 構(gòu)建和部署您的代碼到生產(chǎn)環(huán)境(您可以在不同的平臺上這樣做)

          現(xiàn)今有一些 CI 服務(wù):一些最受歡迎的有Travis、Circle(適用于OSX和Linux)和Appveyor(適用于Windows)。根據(jù)我最初的使用,像 Semaphore CI 這樣的新興產(chǎn)品看起來是可靠的。Gitlab(另一個Git存儲庫管理平臺,如 Github)也支持 CI,不過如同其他服務(wù)一樣,您需要明確配置它。

          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/


          - EOF -
          9d904fb112301cf6c1eac13aa17d6d0d.webp

          Python 進程、線程和協(xié)程實戰(zhàn)指南


          8474b77ddb47ce46c6067ddf993727bc.webp

          全面擁抱FastApi — 藍圖APIRouter

          瀏覽 102
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产第一页在线 | 乱伦一级片 | 先锋影音一区 | 欧美激情中文字幕 | 学生妹一级黄色片久久 |