標籤: 科技

  • 12 個我在工作中學到的生產級 Python 代碼風格

    在工作中,我們編寫的程式碼盡可能地易於人類閱讀。這意味著以下(非詳盡的)列表:

    • 變數名稱具有意義且較長(而非使用 a、b 和 c)
    • 函數名稱具有意義且較長
    • 大量的評論和文件解釋代碼
    • 類型提示無處不在
    • 字串似乎更長且更冗長
    • 等等等等

    這意味著代碼風格將不可避免地演變以適應上述情況。

    因此,以下是我在過去幾年的工作中學到的一些生產級 Python 代碼風格。

    1) 使用括號進行元組解包

    這是一些普通的元組解包:

    在生產級別的程式碼中,我們通常不會使用像 a 或 b 這樣的變數名稱——相反,我們的變數名稱會更長且更具描述性。

    因此,我們可能會使用括號來幫助元組解包,像這樣:

    注意到這樣一來,我們的元組解包可以容納更長(且更具描述性)的變數名稱

    一個更現實的例子:

    2) 多行列表推導式

    以下是普通列表推導式的樣子:

    在生產代碼中,我們通常不使用變量名 i——我們的變量通常更長且更具描述性,以至於我們無法將整個列表推導式塞進一行代碼中。

    我會如何重寫上述代碼:

    一個更現實的例子:

    3) 使用括號組合字串

    生產級的字串通常由於其冗長而無法放入一行中。因此,我們使用括號將它們組合起來。

    注意 — 在括號內,使用引號的字串字面值會自動合併,我們不需要使用 + 運算子來實現這一點

    4) 使用括號輔助的多行方法鏈接

      正常的方法鏈接:

    在生產級別的程式碼中,方法名稱大多數時候會更長,並且我們會將更多的方法鏈接在一起。

    再次,我們使用括號將所有這些內容放入多行中,而不是縮短任何方法名稱或變數名稱。

    請注意,如果我們在括號內進行方法鏈接,則不需要使用 \ 字符來進行明確的換行

    5) 索引嵌套字典

    正常索引嵌套字典的方式:

    這裡有一些問題:

    • 生產級代碼中的字典有更多層次的嵌套
    • 字典鍵的名稱變得更長了
    • 我們通常無法將整個嵌套索引代碼壓縮成一行。

    因此,我們將其拆分為多行,如下所示:

    如果這還不夠,我們將索引代碼拆分為更多行:

    或者如果我們仍然覺得這難以閱讀,我們可以這樣做:

    6) 撰寫易讀且資訊豐富的函數

    我們學生時代如何撰寫函式:

    包含此類代碼的 PR 很可能會被拒絕

    • 函數名稱不具有描述性
    • 參數變數名稱不佳
    • 沒有類型提示,所以我們第一眼無法知道每個參數應該是什麼數據類型
    • 沒有類型提示,所以我們也不知道我們的函數應該返回什麼
    • 沒有文件字串,所以我們必須推斷我們函數的功能

    以下是我們在生產級 Python 代碼中編寫函數的方式

    • 函數名稱應具有描述性
    • 參數名稱應該更具描述性,而不是例如 a, b, c
    • 每個參數都應該有類型提示
    • 函數的返回類型也應包括在內
    • 一個詳細描述函數功能、接收參數及其輸出的文檔字符串應作為三引號內的字符串包含。

    7) 盡可能減少縮排層級

    這是一個 for 迴圈。如果我們的條件被滿足,我們就做某事。

    一些同事和高級工程師可能會對這段代碼吹毛求疵 — 它可以通過減少縮進層次來寫得更好。

    讓我們重寫這個,同時減少 do_something() 的縮進層級

    注意到,僅僅通過使用 `if not condition` 而不是 `if condition`,`do_something()` 的縮進級別就減少了 1 級。

    在生產級別的程式碼中,可能會有更多的縮排層次,而如果層次太多,我們的程式碼就會變得令人煩躁且難以閱讀。因此,這個技巧讓我們能夠使程式碼稍微整潔一些,更易於人類閱讀,

    8) 帶有括號的布林條件

    這是一個使用 and 關鍵字連接三個條件的 if 語句。

    在生產級別的代碼中,條件變得更長,且可能會有更多的條件。因此,我們可以通過將這個龐大的條件重構為一個函數來解決這個問題。

    或者如果我們判斷沒有必要僅僅為此編寫一個新函數,我們可以使用括號來編寫我們的條件語句。

    這樣一來,我們就不必為了這一個條件語句而被迫編寫新的函數或變量,同時我們還能保持代碼的整潔和可讀性。

    有時候我可能真的更喜歡這樣寫,不過這只是基於個人偏好:

    9) 防止 None 值

    正常代碼,訪問對象的某些嵌套屬性。

    這段代碼存在一些可能導致我們的 PR 被拒絕的問題:

    • 如果 dog 是 None,我們會得到一個錯誤
    • 如果 dog.owner 是 None,我們也會得到一個錯誤
    • 基本上,這段程式碼並未防止 `dog` 或 `dog.owner` 可能為 `None` 的情況。

    在生產級代碼中,我們需要積極防範此類情況。以下是我如何重寫這段代碼的方式。

    Python 中的 and 和 or 運算符是短路運算符,這意味著一旦它們得到明確的答案,就會停止評估整個表達式。

    • 如果 dog 是 None,我們的表達式在 “if dog” 處終止
    • 如果 dog 不是 None,但 dog.owner 是 None,我們的表達式會在 “if dog and dog.owner” 處終止
    • 如果我們沒有任何 None 值,dog.owner.name 會成功被存取,並用於與字串 “bob” 進行比較

    這樣一來,我們就能額外防範狗或狗主人可能為 None 值的情況。

    10) 防止遍歷 None 值

    這是我們如何遍歷某些可迭代對象(例如列表、字典、元組等)的方式

    這個問題的一個缺點是它無法防止 mylist 為 None——如果 mylist 恰好是 None,我們會得到一個錯誤,因為無法遍歷 None。

    以下是我如何改進這段程式碼的方式:

    表達式“mylist or None”

    • 如果 mylist 為真值(例如非空可迭代對象),則返回 mylist
    • 如果 mylist 為假值(例如 None 或空的可迭代對象),則返回[]

    因此,如果 mylist 為 None,表達式“mylist or None”會返回[],這樣我們就不會遇到不想要的異常。

    11) 內部函數以 _ 開頭

    這是一個範例類別。在這裡,run 方法使用了其他方法 clean 和 transform

    在生產級代碼中,我們力求盡可能明確,因此嘗試區分內部方法和外部方法。

    • 外部方法——供其他類別和物件使用的方法
    • 內部方法 — 供類別本身使用的方法

    按照慣例,內部方法最好以下劃線 _ 開頭

    如果我們要重寫上述代碼,我們會得到:

    注意 — 在方法名稱前加上底線並不會隱藏它使其對其他類別和物件不可見。事實上,在功能上沒有任何區別。

    12) 用於常見功能的裝飾器

    這是一個包含 3 個函式的類別,每個函式執行不同的操作。然而,請注意,不同函式之間存在相似的步驟——try-except 區塊以及日誌功能。

    減少重複程式碼的一個好方法是撰寫一個包含共同功能的裝飾器函數。

    這樣一來,如果我們想要更新共用程式碼(try-except 和 logging 程式碼),我們不再需要在三個地方進行更新——我們只需要更新包含共用功能的裝飾器程式碼。

  • 什麼是容器運行時?

    Kubernetes 已成為容器編排的首選平台,雖然我們大多數人都能輕鬆地在上面部署和管理應用程式,但理解其背後的魔法有時感覺就像在解讀象形文字。其中一個這樣的概念就是容器運行時。它到底是什麼,為什麼你應該關心?讓我們從基礎開始拆解它。

    什麼是容器運行時?

    什麼是容器運行時?

    簡單來說,容器運行時是一段負責運行容器的軟體。它承擔了拉取容器鏡像、啟動和停止容器以及管理容器生命週期的重任。你可以把它想像成 Kubernetes 汽車引擎蓋下的引擎——你可能不會直接看到它,但沒有它,你就無法前進。

    現實生活的類比

    想像你在一家速食餐廳。你點了餐(容器映像),廚師(容器運行時)準備你的餐點並將其放在托盤上(運行中的容器)。你並不真的在乎櫃檯後面發生了什麼,只要你的漢堡出現就好,但沒有廚師,你只能盯著一個空托盤。

    為什麼我們需要容器運行時?

    容器運行時抽象化了管理隔離進程的複雜性。Kubernetes 使用容器運行時來保持工作負載的一致性、高效性和可移植性。不同的容器運行時可能會影響性能、兼容性和安全性。

    Kubernetes 中流行的容器運行時

    讓我們來看看一些流行的容器運行時,並了解它們如何融入 Kubernetes 生態系統。

    Docker

    容器運行時的鼻祖。它就像那輛一直以來都可靠的老車。Kubernetes 最初依賴 Docker 作為其默認運行時。然而,隨著 Kubernetes 的發展,Docker 被替換為更為精簡的選擇(就像用一輛時髦的新跑車取代你的舊轎車)。

    containerd

    containerd 是從 Docker 本身發展出來的運行時,並成為 Kubernetes 的首選運行時。它更精簡、更快,並且純粹專注於運行容器,而沒有 Docker 所攜帶的額外包袱。

    CRI-O

    專為 Kubernetes 設計,CRI-O 是一款輕量級運行時,與 Kubernetes 的容器運行時接口(CRI)兼容。這就像是升級到混合動力車——高效、專為特定目的打造,並完全兼容現代標準。

    Kubernetes 如何使用運行時?

    Kubernetes 通過容器運行時介面(CRI)與容器運行時進行互動。這使得 Kubernetes 能夠與不同的運行時協作,而不被綁定到特定的運行時。每個節點上的 kubelet 會與運行時通信以管理容器。

    選擇正確的運行時

    在選擇容器運行時,請考慮性能、安全性、兼容性和社區支持等因素。雖然 containerd 通常是預設選項,但 CRI-O 以 Kubernetes 為中心的設計使其成為純 K8s 環境的理想選擇,而 gVisor 和 Kata 則更適合注重安全性的設置。

    結論

    容器運行時可能不是 Kubernetes 中最耀眼的部分,但它們對於讓你的工作負載實際運行至關重要。可以把它們想像成默默無聞的英雄,讓你的集群保持運轉。下次你深入研究 Kubernetes 設置時,不妨向那些默默承擔重擔的容器運行時致以一點敬意。