一. 前言 #
拍拍君每次寫完 Python 準備 commit 的時候,都要跑一輪 flake8、isort、black⋯⋯光等它們跑完就夠喝一杯咖啡了 ☕
後來發現了 Ruff 這個工具,第一次跑的時候差點以為它壞掉了——怎麼一瞬間就跑完了?!
Ruff 是用 Rust 寫的 Python linter + formatter,號稱比 flake8 快 10 到 100 倍。而且它不只是快,還把 flake8、isort、pyupgrade、pydocstyle 等一堆工具的規則整合在一起,一個工具搞定所有事。
今天就來帶大家認識這個讓拍拍君回不去的好東西 🚀
二. 安裝 #
用 pip 安裝 #
pip install ruff
用 uv 安裝(推薦!) #
如果你已經在用 uv,那就更簡單了:
uv tool install ruff
用 Homebrew(macOS) #
brew install ruff
安裝完確認一下版本:
ruff --version
# ruff 0.9.x
三. 基本使用:Lint 檢查 #
最簡單的用法,對整個專案跑 lint:
ruff check .
看到類似這樣的輸出:
app/main.py:3:1: F401 [*] `os` imported but unused
app/utils.py:15:5: E711 Comparison to `None` (use `is None`)
Found 2 errors.
[*] 1 fixable with the `--fix` option.
自動修復 #
很多問題 Ruff 可以自動幫你修:
ruff check --fix .
它會幫你移除沒用到的 import、修正排序等等。加上 --unsafe-fixes 可以處理更多情況(但建議先看一下改了什麼)。
只看某個檔案 #
ruff check app/main.py
四. Format:取代 Black #
從 Ruff 0.1 開始,內建了 formatter,可以直接取代 Black:
ruff format .
想看看它會改什麼但不要真的改:
ruff format --check .
# 或看 diff
ruff format --diff .
Ruff 的 formatter 基本上跟 Black 的風格一致(line length 預設 88),但速度快非常多。
五. 設定檔:pyproject.toml #
Ruff 支援在 pyproject.toml、ruff.toml 或 .ruff.toml 裡設定。拍拍君推薦用 pyproject.toml,跟其他工具放在一起:
[tool.ruff]
# 每行最大長度
line-length = 100
# Python 版本(影響 pyupgrade 規則)
target-version = "py312"
[tool.ruff.lint]
# 啟用的規則集
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"B", # flake8-bugbear
"SIM", # flake8-simplify
"RUF", # Ruff 自己的規則
]
# 要忽略的規則
ignore = [
"E501", # line too long(讓 formatter 處理)
]
[tool.ruff.lint.isort]
known-first-party = ["my_package"]
[tool.ruff.format]
# 用雙引號(跟 Black 預設一樣)
quote-style = "double"
常用規則集一覽 #
| 代碼 | 來源 | 說明 |
|---|---|---|
E/W |
pycodestyle | 基本風格檢查 |
F |
pyflakes | 未使用 import、未定義變數 |
I |
isort | import 排序 |
N |
pep8-naming | 命名規範 |
UP |
pyupgrade | 升級舊語法 |
B |
flake8-bugbear | 常見 bug 模式 |
SIM |
flake8-simplify | 簡化冗餘寫法 |
RUF |
Ruff | Ruff 專屬規則 |
D |
pydocstyle | docstring 風格 |
T20 |
flake8-print | 偵測 print() |
想看完整清單可以跑:
ruff rule --all | head -50
六. 整合 pre-commit #
把 Ruff 加到 pre-commit 裡,每次 commit 自動檢查,再也不會忘記:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
安裝 hook:
pre-commit install
之後每次 git commit 都會自動跑 Ruff lint + format。如果有問題會擋住 commit,幫你守住程式碼品質 💪
搭配 CI/CD #
在 GitHub Actions 裡也很簡單:
# .github/workflows/lint.yml
name: Lint
on: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v2
with:
args: "check"
- uses: astral-sh/ruff-action@v2
with:
args: "format --check"
七. 實戰:從零設定一個專案 #
來看看拍拍君怎麼幫一個新專案設定 Ruff:
# 建立專案
mkdir pypy-demo && cd pypy-demo
uv init
先寫一個「有點亂」的 Python 檔:
# pypy_demo/main.py
import os
import sys
import json
from typing import Optional, List
def calculate_score(name:str,scores:list[int])->float:
total=sum(scores)
avg=total/len(scores)
unused_var = "拍拍君到此一遊"
if avg == None:
return 0
return avg
class chatPTT_analyzer:
def __init__(self, data: Optional[dict] = None):
self.data = data if data != None else {}
def process(self):
result = []
for key in self.data.keys():
if type(self.data[key]) == str:
result.append(self.data[key])
return result
跑一下 lint:
ruff check pypy_demo/main.py
pypy_demo/main.py:1:8: F401 [*] `os` imported but unused
pypy_demo/main.py:2:8: F401 [*] `sys` imported but unused
pypy_demo/main.py:3:8: F401 [*] `json` imported but unused
pypy_demo/main.py:4:29: F401 [*] `typing.List` imported but unused
pypy_demo/main.py:10:5: F841 Local variable `unused_var` is assigned to but never used
pypy_demo/main.py:11:11: E711 Comparison to `None` (use `is None`)
pypy_demo/main.py:15:7: N801 Class name should use CapWords convention
pypy_demo/main.py:17:31: E711 Comparison to `None` (use `is not None`)
pypy_demo/main.py:21:12: E721 Do not compare types, use `isinstance()`
Found 9 errors.
[*] 4 fixable with the `--fix` option.
一次抓出 9 個問題!用 --fix 先自動修掉能修的:
ruff check --fix pypy_demo/main.py
自動移除了沒用的 import,剩下的手動改一下就好。再跑 format:
ruff format pypy_demo/main.py
搞定,乾淨又整齊 ✨
八. 進階小技巧 #
忽略特定行 #
有時候某行就是需要長一點,或者有特殊理由:
import magic_module # noqa: F401 ← 告訴 Ruff 這行不要管
忽略整個檔案 #
在設定檔裡排除:
[tool.ruff.lint]
exclude = ["migrations/", "legacy_code.py"]
輸出 JSON 格式 #
方便跟其他工具串接:
ruff check --output-format json .
看某個規則的說明 #
ruff rule E711
會顯示這個規則的詳細說明跟範例,很適合學習 🎓
結語 #
Ruff 真的是拍拍君近年最愛的 Python 工具之一。它把 linter、formatter、import sorter 全部整合在一起,而且速度快到誇張。
如果你還在用 flake8 + black + isort 的組合,拍拍君強烈建議試試看 Ruff——遷移成本很低,但效率提升非常有感。
一個工具取代一堆工具,快、準、穩。還有什麼好猶豫的呢?😎