我寫這篇文章的原因是分享一些關於保持專案乾淨的見解,即使有很多貢獻者。考慮到資料不斷變化的性質以及 Python 程式庫和應用程式中的處理需求,這一點對於資料工程師來說尤其重要。
標題可能聽起來有點標題黨,但如果您遵循這些步驟,您的 Python 程式碼將變得更容易管理。如果您是高級開發人員,您可能不會在這裡找到任何新東西 - 別擔心,我為您準備了一個有趣的模因。
這可能看起來微不足道,但我實際上認識一些人,他們只將程式碼儲存在本地電腦上 - 不幸的是,由於他們沒有在其他地方備份它,所以丟失了程式碼。有許多可用的版本控制系統,例如 Git,它可與 GitHub、GitLab 和 Bitbucket 等平台搭配使用。雖然 Git 是許多人的首選,但 SVN (Subversion) 和 Mercurial 等其他版本控制系統仍然在程式碼管理中發揮重要作用。
在本指南中,我打算建立一個小型示範 Python 函式庫,其中包含一個函數來說明基本資料處理。它並不是一個完整的工具包,而是作為一個簡單的範例來演示程式碼品質、環境管理和 CI/CD 工作流程等最佳實踐。
首先,我為我們的示範 Python 庫建立了一個儲存庫,並將其命名為 blog_de_toolkit。它是公開的,因此請隨意分叉它並使用現有程式碼作為您自己的專案的起點。由於良好的文檔至關重要,因此我提供了一個建議的空自述文件。為了保持倉庫整潔,我添加了一個預設的 Python .gitignore 模板,以防止上傳不必要的檔案。
現在我們有了儲存庫,我們可以複製它。
我不打算在這裡深入探討 Git 指令——你可以在網路上找到大量教學。如果您不喜歡使用普通終端 CLI,您也可以使用 GitHub Desktop 或 SourceTree 等工具來管理儲存庫,它們提供了更直覺的介面。
就我個人而言,我非常喜歡使用 GitHub Desktop。因此,讓我們將儲存庫克隆到本機電腦並在您喜歡的 IDE 中開啟它。
讓我們看看到目前為止我們得到了什麼:
開始還不錯!
對於第 2 步,我們將組織我們的 de_toolkit 項目。良好的結構可以輕鬆找到東西並保持一切整潔。我們將為程式碼、測試和文件建立資料夾,設定一個簡單、乾淨的框架來建置。
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
我們有一個主資料夾來存放我們將要添加的所有有用程式碼,一個tests 資料夾用於我們未來的單元測試,以及一個.gitignore 將不必要的檔案保留在我們的存儲庫之外。還有一個 setup.py 文件,這是使專案可安裝的基本設定。我現在不會詳細介紹它,因為我們稍後將在第 8 步:創建分發包中介紹它。
在設定專案結構時,保持一致會產生巨大的差異。隨著專案的發展,最好將其分解為更小的模組,例如將 data_tools.py 拆分為 csv_tools.py 和 json_tools.py。這樣,您就可以更輕鬆地管理和找到所需內容,而無需翻閱長文件。
新增 docs/ 資料夾也是一個明智之舉,即使它只是以一些註解開頭。它將幫助您(和其他人)隨著專案的發展追蹤事情的運作方式。如果您正在使用 YAML 或 JSON 等配置,configs/ 資料夾可以幫助保持整潔。如果您打算編寫自動化或測試腳本,scripts/ 資料夾將使它們井然有序。
此時,我們需要安裝一些額外的函式庫來繼續建置專案。
當然,我們可以從命令列執行 pip install 來安裝我們需要的依賴項。但是,如果我們要處理多個項目,每個專案都需要不同版本的 Python 和函式庫怎麼辦?這就是虛擬環境的用武之地——它們隔離每個專案的依賴關係,包括特定的 Python 版本,因此一切都保持獨立和獨立。
幸運的是,有許多工具可以創建虛擬環境,因此您可以選擇最適合您的工具:
virtualenv
venv
康達
pyenv
pipenv
詩歌
就我個人而言,我是 pyenv 的忠實粉絲,所以這就是我將在這裡使用的。我已經將它安裝在我的筆記型電腦上,因為它是我工作和個人專案的首選。
讓我們從安裝 Python 開始:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
如果 pyenv 無法辨識此 Python 版本,請先嘗試更新它。例如,如果您使用的是 Mac 並使用 Homebrew 安裝了 pyenv,請執行:
pyenv install 3.12.2
如果遇到錯誤 ModuleNotFoundError:沒有名為 '_lzma' 的模組,請嘗試:
brew update && brew upgrade pyenv
接下來,在我們的專案資料夾中,建立一個新的虛擬環境:
brew install readline xz
現在,將本機Python版本設定為您剛剛建立的虛擬環境:
pyenv virtualenv 3.12.2 de_toolkit
如果在 MacOS 上執行指令後環境沒有切換,網路上有一個有用的執行緒提供了解決方案。一旦一切設定正確,您應該在命令列開頭看到 de_toolkit,如下所示:
現在,讓我們安裝我們的依賴項:
pyenv local de_toolkit
接下來,我們將把所有已安裝的軟體包及其版本儲存到requirements.txt 檔案中。這使得共享專案的依賴項或在其他地方重新創建相同的環境變得容易:
pip install setuptools wheel twine pandas
這是我們得到的已安裝軟體包的清單:
當然,如果您願意,您可以編輯requirements.txt 檔案以僅保留主庫及其版本。
這一步至關重要—可能是最重要的步驟之一。您可能聽說過有關 GitHub 儲存庫中的憑證被洩露或敏感令牌意外在公共場合共享的恐怖故事。為了避免這種情況,必須從一開始就將敏感資訊排除在程式碼之外。否則,很容易忘記您硬編碼了資料庫密碼,推送了更改,然後突然——您的憑證現在公開了。
硬編碼密碼、API 金鑰或資料庫憑證是主要的安全風險。如果它們進入公共倉庫,它們可能會損害您的整個系統。處理秘密的最安全方法是將它們儲存在環境變數或 .env 檔案中。為了幫助將這些變數載入到您的 Python 專案中,我們將使用 python-dotenv 函式庫。它從 .env 檔案中讀取鍵值對,並將它們用作程式碼中的環境變數。
首先,安裝庫:
pip freeze > requirements.txt
在專案資料夾中建立一個 .env 文件,其中包含以下內容:
pip install python-dotenv
現在,讓我們修改 data_tools.py 以使用 python-dotenv:
載入這些機密當您呼叫 load_dotenv() 時,它會在目前目錄中搜尋 .env 檔案並將其內容載入到環境中。使用 os.getenv() 可讓您在程式碼中安全地存取這些變量,使憑證與原始程式碼隔離並降低意外暴露的風險。
一個關鍵提示是避免將 .env 檔案提交到版本控制。將其新增至 .gitignore 以防止意外推送:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
如果您使用 VSCode,有一個有用的 dotenv 擴充功能可以自動識別您的 .env * 檔案。如果您喜歡從終端工作,您可以匯出 *.env 文件,如下所示:
pyenv install 3.12.2
在處理專案時,嘗試編寫易於理解和管理的小型、可重複使用的函數。一個好的經驗法則是:「如果您使用它超過兩次,請將其變成一個函數。」
在我們的 data_tools.py 中,我們建立一個函數來示範典型的資料工程邏輯,例如從 CSV 載入資料並清理它:
專業提示:在 Python 中堅持使用 snake_case
作為函數和變數名稱 - 它可以使您的程式碼保持一致且易於閱讀。避免使用 x 或 df2 等神秘名稱;清晰的描述性名稱使您的程式碼更易於使用。我們在這裡使用文件字串來描述函數的功能、參數和傳回類型。這使得其他開發人員(以及未來的你)可以輕鬆了解如何使用該功能。有幾種流行的文檔字串約定,但最常見的包括 PEP 257、Google Style 和 NumPy Style:
對於較小的函數,PEP 257 通常就足夠了,但對於更複雜的項目,Google 或 NumPy 樣式提供了更多的清晰度和結構。
Python 中的類型提示(如我們範例中的 file_path: str)顯示函數輸入和輸出的預期資料類型。它們提高了可讀性,幫助發現錯誤,並透過設定明確的期望使協作變得更容易。
以下是類型提示如何改進函數簽章的範例:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
在這個範例中,類型提示 file_path: str 表示參數應該是一個字串,而 ->; pd.DataFrame 指示函數傳回一個 Pandas DataFrame。這使得該函數的行為一目了然。類型提示還可以與 IDE 和 linter(例如 PyCharm、VSCode 或 mypy)很好地配合使用,在傳遞不相容的類型時提供自動完成和早期警告。
如果函數可以傳回多種型別或無,您可以使用輸入模組中的可選:
pyenv install 3.12.2
這表示該函數可以傳回字串或 None。對於更複雜的資料結構,您可以使用輸入模組中的 List、Dict 或 Tuple 來指定預期類型。
編寫單元測試是一種簡單的方法,可以確保您的程式碼執行預期的操作,而不會出現意外的情況。它們可以幫助您及早發現錯誤並充滿信心地進行更改,因為您知道一切仍然按預期運行。在 Python 中,有多個可用於單元測試的函式庫,每個函式庫都有其優點和生態系統:
單元測試
pytest
鼻子2
假設
對於本指南,我們將使用 pytest 因為它簡單、靈活且易於使用。您可以使用以下命令安裝它:
brew update && brew upgrade pyenv
接下來,在 tests/ 資料夾中建立一個名為 test_data_tools.py 的檔案。讓我們為之前實現的程式碼編寫一些測試。這是我們的 load_and_clean_data() 函數和環境變數檢索邏輯的範例測試:
在 test_load_and_clean_data() 中,我們使用 StringIO 模擬 CSV 檔案作為輸入。這使我們無需實際文件即可進行測試。此測試驗證函數是否正確刪除重複項和 NaN 值,檢查 DataFrame 是否沒有遺失數據,並確認「名稱」列中的唯一條目正確。
在test_get_database_url()和test_get_api_key()中,我們使用pytest提供的monkeypatch工具來在測試過程中暫時設定環境變數。這確保函數傳回預期值,而不需要真正的環境變數。
要執行所有測試,只需執行以下命令:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
我喜歡pytest的原因之一是它的靈活性。它超越了基本的單元測試,提供了強大的功能,如夾具、參數化測試和插件。夾具可讓您設定多個測試可重複使用的測試資料或配置,讓您的程式碼保持乾燥(不要重複)。參數化測試可讓您使用不同的輸入來執行相同的測試,從而節省時間並減少重複。如果您需要擴展 pytest 的功能,可以使用廣泛的插件生態系統,例如測試 Django 應用程式、測量程式碼覆蓋率或模擬 HTTP 請求。
保持高程式碼品質可確保您的程式碼易於閱讀、維護且沒有常見錯誤。有多種工具可以幫助實施一致的編碼標準、自動格式化程式碼並及早偵測潛在問題。一些流行的選項包括 pylint、flake8、黑色 和 檢測秘密。
pylint 強制執行編碼標準並捕捉常見錯誤。
flake8 結合了偵測樣式違規和邏輯錯誤的工具。
black 是一個固執己見的格式化程序,可確保您的程式碼遵循 PEP8 標準。
偵測秘密掃描您的程式碼以防止硬編碼的秘密被洩露。
您可以透過以下方式安裝這些工具:
pyenv install 3.12.2
例如,在特定檔案或目錄上執行 pylint:
brew update && brew upgrade pyenv
您將收到一份報告,其中包含警告和改進程式碼的建議。要忽略特定警告,您可以使用:
brew install readline xz
您也可以使用flake8來找出樣式問題和邏輯錯誤:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
要自動格式化程式碼,請執行 black:
pyenv install 3.12.2
您可以使用預先提交掛鉤自動執行該過程,而不是每次進行更改時手動運行這些工具。預提交掛鉤在每次提交之前自動運行,如果任何工具失敗,則會阻止提交。
首先,安裝預先提交軟體包:
brew update && brew upgrade pyenv
接下來,在專案目錄中建立一個 .pre-commit-config.yaml 文件,其中包含以下內容(這裡我使用了所有我最喜歡的基本預提交):
啟動本機儲存庫中的預提交掛鉤:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
現在,每次您嘗試提交時,這些工具都會自動運行。如果任何工具失敗,提交將被阻止,直到問題解決。您也可以在程式碼庫中手動執行所有掛鉤:
pyenv install 3.12.2
現在我們已經建立了我們的項目,編寫了一些程式碼,添加了測試並設定了預提交掛鉤,下一步是弄清楚其他人(甚至未來的我們)如何輕鬆使用它。打包項目使這成為可能。它允許我們將所有內容整齊地捆綁在一起,以便無需手動複製文件即可安裝和使用。
要分享您的項目,您需要正確建置套件、編寫有意義的自述文件、建立啟動腳本並產生分發包。一個好的自述文件通常包括專案名稱和其功能的簡要描述、安裝說明、使用範例、設定環境的開發說明以及貢獻指南。您可以在儲存庫中找到我們的 blog_de_toolkit 專案的簡單 README.md 範例。
任何 Python 套件的核心都是 setup.py 檔案。該文件是我們定義打包和安裝專案所需的元資料和配置的地方。它包括項目的 名稱、版本和描述,這使其可識別。 long_description 從 README 文件中讀取內容,以便當用戶在 PyPI 上看到該項目時為他們提供有關該項目的更多背景信息。我們在 install_requires 清單中指定依賴項,以便它們與套件一起自動安裝。 entry_points 部分定義了命令列介面 (CLI) 條目,以便使用者可以從終端運行該工具。我們使用 find_packages() 來包含套件中的所有子模組,並且 classifiers 部分提供元數據,例如專案使用的 Python 版本和授權。最後,python_requires 欄位確保套件僅安裝在相容的 Python 版本上。這是我們的 blog_de_toolkit 專案的 setup.py 設定:
配置setup.py後,您可以建立分發包。先安裝必要的工具:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
然後建構套件:
pyenv install 3.12.2
此指令建立兩個分發檔案:
sdist:來源存檔(例如 .tar.gz)
bdist_wheel:建置的套件(例如.whl)
這些檔案將位於 dist/ 目錄中。要測試該套件,請使用以下命令在本地安裝它:
brew update && brew upgrade pyenv
您也可以透過執行來測試 CLI 指令:
brew install readline xz
他應該列印資料庫 URL、API 金鑰以及來自 sample_data.csv.
的已清除資料如果您想公開分享該包,您可以將其上傳到PyPI。首先,安裝 Twine:
pyenv virtualenv 3.12.2 de_toolkit
然後上傳套件:
pyenv local de_toolkit
系統將提示您輸入 PyPI 憑證。上傳後,其他人可以直接從 PyPI 安裝您的套件:
pip install setuptools wheel twine pandas
隨著您的專案的發展,更多的人通常會同時在同一個程式碼庫上工作。如果沒有適當的保護措施,錯誤、未經測試的程式碼或意外合併很容易潛入並造成混亂。為了保持事情順利進行並保持高標準,保護主幹就變得至關重要。在此步驟中,我們將了解如何設定分支保護規則,並分享一些對拉取請求進行順利程式碼審查的技巧。
分支保護規則確保任何人都無法在未通過測試或程式碼審查的情況下直接推送到主分支。這可以防止未完成的功能、錯誤或不良程式碼潛入並破壞專案。它還透過要求拉取請求來促進團隊合作,讓其他人有機會提供回饋。另外,自動檢查(如測試和 linter)可確保程式碼在合併之前是可靠的。
在 GitHub 上設定分支保護規則非常簡單。前往儲存庫的設定,然後按一下「程式碼與自動化」部分下的分支。尋找分支保護規則並點選新增分支保護規則。在分支名稱欄位中輸入 main,現在是時候調整一些設定了。
您可以設定分支保護規則來要求拉取請求審查,確保有人在合併之前檢查程式碼。狀態檢查可確保測試通過並且 linter 順利運行,並使分支保持最新變更有助於避免衝突。如果需要,您可以限制誰可以推送到分支,甚至需要簽名提交以提高安全性。一切設定完畢後,點擊建立,就像這樣 - 不再直接推送或跳過測試。
當您的拉取請求接受審核時,最好讓審核者感到輕鬆。首先清楚地描述您的更改的作用以及為什麼需要它們。使用有意義的提交訊息來反映已更新的內容。如果更改較小且重點突出,則審核過程會變得更加順利和更快。不要忘記禮貌地回覆評論並跟進請求的更改 - 這表明您重視反饋並有助於保持積極的合作。
如果您是審查拉取請求的人,那麼您的工作不僅僅是發現錯誤 - 還包括改進程式碼並支援您的隊友。首先閱讀拉取請求描述,以了解更改試圖實現的目標。專注於提供建設性回饋——如果需要,建議替代方案,並解釋為什麼它們可能效果更好。透過簡單的「重構不錯?!」來認可優秀的工作也有助於創造正面的評論體驗。密切注意測試,確保它們存在、相關且通過。如果有些事情不清楚,請提出問題而不是做出假設。歸根結底,評論是關於團隊合作的——合作使專案變得更好。
使用審核範本可以讓每個人都專注於重要的事情,從而使流程更加順利。以下是拉取請求審核範本的範例:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
將這樣的範本加入到您的貢獻指南中或將其連結到儲存庫中,可以讓審閱者輕鬆保持在正軌上。它還使評論之間的內容保持一致,幫助團隊維護乾淨且有組織的程式碼庫。
基於保護主分支的重要性,確保在合併或部署之前對每個程式碼變更進行正確的測試、審查和驗證也至關重要。這就是持續整合(CI)和持續交付/部署(CD)發揮作用的地方。 CI/CD 自動化執行測試、執行程式碼檢查和部署變更的流程,為開發人員提供快速回饋並減少錯誤進入生產的機會。
GitHub Actions 是直接整合到 GitHub 的自動化工具。它使您能夠建立回應儲存庫中的事件(例如推送或拉取請求)的工作流程。在 GitHub Actions 中,我們可以自動執行多項關鍵任務以維護健康的程式碼庫。例如:
每當推送程式碼或建立拉取請求時執行測試,確保新的變更不會破壞任何內容。
檢查程式碼樣式和 linting 以執行一致的編碼標準。
應用預提交掛鉤來格式化程式碼並捕獲尾隨空格等小問題。
在所有檢查通過後產生文件甚至部署程式碼。
讓我們設定一個 GitHub Actions 工作流程,執行我們的單元測試,並在主分支上發生推送或拉取請求時應用預提交 linter(如黑色)。
首先,建立工作流程檔案:
blog_de_toolkit/ │ ├── de_toolkit/ │ ├── __init__.py │ └── data_tools.py │ ├── tests/ │ ├── __init__.py │ └── test_data_tools.py │ ├── .gitignore ├── setup.py └── README.md
這是ci.yml的內容:
每當在主分支上推送程式碼或開啟拉取請求時,此工作流程都會自動進行測試和檢查。它確保在合併代碼之前通過所有品質檢查。 actions/checkout 操作將儲存庫複製到執行器中,我們使用 actions/setup-python 為工作流程設定 Python 3.12。依賴項是使用 pip 從requirements.txt 安裝的。之後,所有測試都使用 pytest 運行,預先提交掛鉤確保程式碼遵循格式和樣式指南。如果任何測試或檢查失敗,工作流程就會停止,以防止損壞的程式碼合併。
讓我們來測試一下。首先,從主分支建立一個新分支並進行一些更改。
就我而言,我更新了自述文件文件。提交您的變更並向主分支開啟拉取請求。
現在您將看到需要進行審核,而 GitHub Actions (GA) 正在執行所有檢查。即使合併被分支保護規則阻止,我仍然可以「無需等待滿足要求即可合併」,因為我的權限允許繞過保護。
您可以在 Actions 標籤中追蹤 GitHub Actions 工作流程的結果。
以下是運行期間 pytest 步驟的範例:
手動追蹤專案的版本可能會變得混亂,尤其是隨著專案的成長。這就是 語意版本控制 (SemVer) 的用武之地 - 它遵循 MAJOR.MINOR.PATCH 模式來傳達每個版本中發生的變更。使用 python-semantic-release 自動進行版本控制使這變得更加容易。它會分析您的提交訊息,根據更改類型、標記版本來升級版本,如果需要,甚至可以將您的套件發佈到 PyPI。這消除了版本管理中的猜測並確保了一致性。
為了實現無縫版本控制,您可以將 python-semantic-release 直接整合到 GitHub Actions 中。官方文件提供了每當您推送到主分支時自動進行版本更新和發布的工作流程。透過此設置,發布過程變得順暢且無需幹預,因此您可以專注於編寫程式碼,而不必擔心手動管理版本。
常見工作流程範例 — python-semantic-release
為了實現此目的,您的提交訊息需要遵循傳統的提交標準。每種類型的提交決定版本是否會提升 PATCH、MINOR 或 MAJOR 等級:
修正:觸發 PATCH 版本更新(例如 1.0.0 → 1.0.1)。
壯舉:觸發次要版本更新(例如,1.0.0 → 1.1.0)。
重大變更:提交訊息中的 或 ! 會觸發主要版本更新(例如,1.0.0 → 2.0.0)。
遵循這些簡單的約定,您將始終知道每個新版本會發生什麼。
我們涵蓋了從組織專案和管理機密到編寫測試、自動化工作流程以及使用語義版本控制處理發布的所有內容。有了正確的工具和流程,建立可靠且可維護的專案就會變得更加順利,甚至充滿樂趣。
關鍵是保持一致,盡可能實現自動化,並不斷改進。隨著時間的推移,每一個小步驟都會產生很大的變化。現在輪到你了——去建造、實驗並享受這個過程!嘗試將這些步驟應用到您的下一個項目中 - 並隨時在評論中分享您的經驗!
以上是為初學者組織和維護 Python 程式碼庫的步驟的詳細內容。更多資訊請關注PHP中文網其他相關文章!