大概在三年半前
我曾經寫過 Python 3.8 的環境建設
隨著時間的快速推進
現在 Python 版本已經來到了 3.13-dev
因此今天就花點時間 Update 一下我們的資訊吧

# 前言

Python 是一門很有趣的程式語言
雖然在效能上的消耗遠不如 Golang 或 Rust 等語言
但在社群支援度和套件豐富度上
Python 的強悍足以和 NodeJS 的生態系相比擬
擁有趨近於予取予求的現成套件量

經過了將近四年的磨練後
筆者也算是對 Python 與環境有了更多的認識
本篇文章就手把手的教你如何建立 Python 的開發環境

# 觀前提示

  • 本篇文章將主要以 CLI 方式進行 若有使用上的需求可以參考 CLI 工作訣竅
  • 本文適用於主流的 MacOS、Linux、Windows 系統
  • 本文將以 Python3.12 為案例進行說明 若需求不同請自行調整
  • 請務必知道你自己在做什麼 希望你不要把電腦搞壞

# Python 環境的安裝

# Python 本體

根據使用的作業系統不同
Python 的安裝方式也會有所不同
以下將分別簡單介紹

Windows 系統中有兩種安裝模式

  1. 直接至官網下載安裝包
  1. 透過 Microsoft Store 安裝

MacOS 系統下內建就有安裝 Python
需要更新的時候更新就好
如果有需求要安裝
我們有兩種方式可以進行安裝

  1. 透過 Homebrew 安裝
brew install python
  1. 透過官網下載安裝包

絕多數的 Linux 發行版中都會自帶 Python
如果沒有安裝的話
依照系統的不同 我們可以在 Terminal 中輸入

sudo apt-get install python3 # Ubuntu
sudo yum install python3 # CentOS
sudo pacman -S python3 # Arch
sudo dnf install python3 # Fedora

# Python 套件管理器

Python 的特色之一便是擁有豐富的套件
而這些套件的管理基本上會透過 pip 這個套件管理器進行

# 安裝 pip

在 Python3.4 之後 pip 已經內建在 Python 裡了
如果你的 Python 版本是 3.4 以上
恭喜你 我不太覺得現在會有人用這麼舊的版本
代表你的 pip 已經安裝只需要更新即可

pip install --upgrade pip # 更新 pip
python3 -m pip install --upgrade pip # 如果你是 windows 的話

# 嘗試安裝套件

我們可以嘗試安裝一些套件來測試 pip 是否正常運作

pip install requests # 安裝 requests 套件

接著隨便建立一個 app.py
試著測試我們的套件是否可以正常運作

import requests # 引入 requests 套件
print(requests.get('https://www.google.com').status_code) # 對 Google 發送請求並印出狀態碼

如果沒有 在import階段 就報錯
那代表我們的套件是有引用成功的
如此一來代表我們的 Python 環境已經建立完成了

# Requirements.txt 與套件依賴

在 Python 的開發中
往往不會只需要一個套件就能夠完成開發
相反地 套件一裝總是就會有很多個
而這些套件之間的可能也會彼此依賴
依賴關係也是一個很大的問題
這時候我們就可以使用 requirements.txt 來進行套件的管理

pip freeze > requirements.txt # 將當前的套件列表寫入 requirements.txt
pip install -r requirements.txt # 根據 requirements.txt 安裝套件

requirements.txt 的內容會長得像這樣

requests==2.26.0

這個檔案會幫你記錄你需要什麼樣的套件
以及需要什麼樣的版本以完成任務
這樣一來就可以避免套件的版本衝突問題了
並且在進行板控時
可以直接使用這個檔案來安裝套件
不需要將整個套件包整個上傳也能夠完成安裝

# 大問題

我們的小明今天安裝了一個大套件
然而他發現他裝錯了
於是他從他的專案中刪掉這個套件
問題來了 這個某些依賴受到影響並互相干擾
並且其他專案也無法使用這個套件了
此時應該怎麼做呢

# 虛擬環境

對於環境衝突的問題
如果熟悉 NodeJS 的使用者應該知道
NodeJS 會以專案為單位進行套件的管理
而使用 package.json 進行套件的記錄
並將套件安裝在專案資料夾的 node_modules

然而預設的 Python 並不會這麼做
使用 pip 安裝的套件將會全部安裝在 Python 的套件資料夾中
此時 我們可以利用虛擬環境來解決這個問題

# 挑選虛擬環境

Python 的虛擬環境有很多款
包括但不限於

  • venv
  • conda
  • pipenv
  • poetry

筆者我本篇的文章將會以 poetry 為主
因此將會介紹僅會簡單介紹其他款
並將本期文章的重點將會著重在後者的使用上

# 其他虛擬環境簡介

venv 是 Python 內建的虛擬環境
使用起來非常方便

python3 -m venv venv # 創建虛擬環境
source venv/bin/activate # 啟動虛擬環境
deactivate # 關閉虛擬環境

這樣一來我們就可以在虛擬環境中進行套件的安裝
將虛擬環境以專案為單位進行隔離
如此一來即可避免環境混淆的問題

然而 venv 的問題在於
它實在是太土砲了
僅僅只有虛擬環境的功能
並沒有一個很好的辦法去控管我們的環境設置和套件相容

conda 是一個市場蠻廣泛但我個人並不常使用的虛擬環境
有許多玩 AI 的開發者會使用這款管理器
對套件的支持度也相當不錯

conda create -n myenv python=3.12 # 創建虛擬環境
conda activate myenv # 啟動虛擬環境
conda deactivate # 關閉虛擬環境

這樣一來我們就可以在虛擬環境中進行套件的安裝
然而為什麼如此強大的套件管理器我個人並不常用呢
原因是因為它太肥了
就如同 visual studio 一樣
好用歸好用 但平常進行小專案開發
並不會想要殺雞用牛刀
conda 的巨量套件和龐大的安裝包成為了它的致命傷

pipenv 是一個為了解決 Pip 問題而誕生的套件管理器

  • 自動生成 requirements.txt
  • 自動生成 Pipfile
  • 自動生成 Pipfile.lock

使用上也很簡單

pipenv install requests # 安裝 requests 套件
pipenv shell # 進入虛擬環境
exit # 退出虛擬環境

然而 這款雖然跟我們要介紹的 Poetry 有點類似
但卻有著 Lock 太慢、Windows 支援度差、對 PyPI 打包支援度很差的種種問題
同時社群維護又不夠好
因此也被眾多 Poetry 的使用者所詬病

# poetry

這是今天的重中之重
poetry 是一個專為 Python 開發者打造的套件管理器
筆者我本人平時也是使用這牌的環境管理器進行開發作業
這個套件管理器的特色在於

它就是很好用

咳 這樣講可能有點籠統
簡單來說 Poetry 具有一下優點

  • 擁有非常好的規範和拓展性
  • 對於環境管理的安全性和可移植性也有很高的上限
  • 最重要的是 安裝非常的簡單直白、輕量且容易上手
  • 社群維護度高 官方文檔清楚

並且 Poetry 可以說整合了 Pip 和 venv 的功能
於是乎 我們就來一探這厲害又方便的套件管理器吧

# 安裝

Poetry 的安裝在官方文檔中有非常詳細的說明
幾乎不需要額外配置 只需要安裝完成即可使用

pipx install poetry

# 使用 curl

curl -sSL https://install.python-poetry.org | python3 -

# 使用 powershell

(Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py -

依照官方安裝的腳本
預設的路徑會是

  • $HOME/.poetry/bin : Unix
  • %appdata%\Python\Scripts : Windows

安裝時是可以設定安裝路徑的
只是如果你直接使用上述的指令安裝的話應該都會在預設位置
此時我們可以修改一下環境變數來讓我們的 Poetry 更方便使用

export PATH="$HOME/.poetry/bin:$PATH" # Unix
$env:Path = "$env:Path;$HOME\.poetry\bin" # Windows

如此一來我們就可以直接在任何地方直接使用指令召喚 Poetry 啦

# 初始化專案

首先我們進入專案資料夾內

mkdir myproject # 創建專案資料夾
cd myproject # 進入專案資料夾

隨便開一個新的檔案結構

filetree
$ tree
.
├── app.py
└── src/
1 directory, 1 files

此時 重點來了
我們需要對專案進行初始化

poetry init

緊接著會跳出一連串的互動式問題
這些是幫助你提前設定好你的專案的基本資訊
如果你看不懂的話可以無腦的按 enter 下去沒有關係
這些內容在未來也可以再做修改

問題大概長這樣(點擊展開)
$ poetry init
This command will guide you through creating your pyproject.toml config.
Package name [poetry-demo]:
Version [0.1.0]:
Description []:
Author [miyago9267 <mail>, n to skip]:
License []:
Compatible Python versions [^3.12]:
Would you like to define your main dependencies interactively? (yes/no) [yes]
You can specify a package in the following forms:
  - A single name (requests): this will search for matches on PyPI
  - A name and a constraint (requests@^2.23.0)
  - A git url (git+https://github.com/python-poetry/poetry.git)
  - A git url with a revision (git+https://github.com/python-poetry/poetry.git#develop)
  - A file path (../my-package/my-package.whl)
  - A directory (../my-package/)
  - A url (https://example.com/packages/my-package-0.1.0.tar.gz)
Package to add or search for (leave blank to skip):
Would you like to define your development dependencies interactively? (yes/no) [yes]
Package to add or search for (leave blank to skip):
Generated file
[tool.poetry]
name = "poetry-demo"
version = "0.1.0"
description = ""
authors = ["miyago9267 <mail>"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Do you confirm generation? (yes/no) [yes]

此時你會看到互動問題的最後面
提示你是否確認生成一個 pyproject.toml 檔案
確認後再重新檢查一次檔案結構可以看到一個新的檔案出現在專案中

filetree
$ tree
.
├── app.py
├── pyproject.toml
└── src/
2 directories, 2 files

理論上此時我們的專案就初始化完畢
可以開始進行套件的安裝和開發流程了
但此時 麻煩先幫我打上一條指令
理由等等會再做解釋

poetry config virtualenvs.in-project true # 強制 venv 生成於專案內

# 套件安裝

都完成後我們就可以開始安裝我們需要的套件進行開發了
我們可以這樣去從 PyPI 的套件庫尋找並安裝我們需要的套件

poetry add <package> # 安裝套件
poetry add requests # 嘗試安裝 requests 套件

並可以使用以下指令來查看我們的套件列表

poetry show # 查看套件列表

此時我們的 pyproject.toml 會發生變化

[tool.poetry.dependencies]
python = "^3.11"
requests = "^2.31.0"

可以看到在 [tool.poetry.dependencies]
多了一個 requests 的套件
以及記錄著版本下限
代表著之後如果要引用會從這裡調用資料進行安裝

並且除了原本的 pyproject.toml 檔案外
系統還會多產生一個 poetry.lock 檔案
這個檔案實際上就相當於 pip 中的 requirements.txt
這個檔案會記錄著我們的套件的當前安裝的版本和內容
絕多數時候裡頭的內容取決於 pyproject.toml 的內容
不過有時候手動修改 pyproject.toml 的內容時
例如手動的套件更新 或者新增了額外的安裝鏡像
我們可以透過

poetry lock # 更新 poetry.lock

進行依賴文件的更新

# 虛擬環境

基本上在安裝套件的同時
Poetry 就會自動幫我們生成一個虛擬環境
若尚未安裝套件的話
則會在進入虛擬環境後自動安裝

在 Poetry 中
和其他虛擬環境管理軟體一樣
我們也可以使用以下指令來進入虛擬環境

poetry shell # 進入虛擬環境
exit # 退出虛擬環境
poetry run <command> # 在虛擬環境中執行指令

在執行 poetry shell
會檢查當前環境是否存在於專案內
若否的話則會噴錯給你看喔

# 虛擬環境的位置

前面提到 Poetry 在進入虛擬環境時會產生一份自己的虛擬環境資源
然而 這個環境會被放在一些很神奇的位置
並且會害你在調整版本時由於檔案位置的緣故摸不著頭腦
因此 我個人習慣會使用上面提過的指令
來讓一份虛擬環境強制放入專案內
使得專案與環境的映射更加明確

poetry config virtualenvs.in-project true # 強制 venv 生成於專案內

如果不小心沒有設定的話也不要緊
我們可以透過以下指令來查看我們的虛擬環境的位置

poetry env info # 查看虛擬環境位置

之後再透過以下的指令來進行設定

poetry env remove <path> # 移除虛擬環境
poetry config virtualenvs.in-project true # 強制 venv 生成於專案內
poetry env use python # 設定虛擬環境位置

# 輸出 requirements.txt

雖然在 Poetry 中我們已經有了 poetry.lock 檔案
並且功能幾乎與 requirements.txt 等價
但並非代表我們不需要 requirements.txt
有時候我們需要將我們的專案交給其他人
或者需要包成 docker 等服務時
我們不可能要求目標環境也安裝 Poetry

因此我們可以透過以下指令來將我們的 poetry.lock 輸出成 requirements.txt

poetry export -f requirements.txt -o requirements.txt --without-hashes
注意

預設的輸出檔案中會有 hash 值影響閱讀
後面的 -without-hashes 會將 requirements.txt 中的 hash 值去除
但有時候 Hash 會有重要的功用
因此後面的參數要視情況做使用
參考資料 - Github Issue

# 上版後的操作

今天假設我們在遠端開發了一個段落
我們將專案推上了 Git 的遠端儲存庫
並且在另一台電腦上進行了 Clone
理論上 我們並不會把 .venv__pycache__ 等檔案推上去
因此我們在 Clone 下來後是沒有裝好的虛擬環境的

此時我們當然不用一步一步重新安裝過一次
而是可以用以下指令
直接調用已存在的 pyproject.tomlpoetry.lock 來進行安裝

poetry install

此時 Poetry 會自動幫我們安裝我們在 pyproject.toml 中所記錄的套件
並且會自動重新生成一份虛擬環境的資料
如此一來我們就能在另一台電腦上使用環境啦

# Pyenv 的相容性

這裡是一段小小的實用工具補充
在 NodeJS 中有所謂的 NVM 可以進行 Node 的版控切換
而在 Python 中也有一個類似的工具

叫做 Pyenv

Pyenv 是一個可以讓你在同一台電腦上安裝多個 Python 版本的工具
並且可以進行系統、全域、本地版本的切換
如果同一系統中同時有需要新、舊版本的專案需求時
這會是一個很好用的版本控制器
並且能夠一定程度上與 Poetry 相容
不過礙於主題與篇幅問題
本文並不會展開解說
附錄中會放官方的 Github 文檔
有興趣可以自行研究

# 結語

今天我們學習了如何在 Python 中建立一個開發環境
並且學習了如何使用 Poetry 進行套件的管理

下一期應該會正式開啟一些 Python 的基礎教學
不過重點並不會著重在程式語言的基本語法和觀念
而是談一談一些在 Python 開發中值得分享的眉角
那麼下次見啦

# 參考資料

# 附錄