跳到主要內容

Web Day 4 FastAPI 起步:路由、Pydantic v2 與自動文件

Web Day 4 FastAPI 起步:路由、Pydantic v2 與自動文件

執行需求:CPU 可跑。經過前三天把地圖、環境、HTTP/REST 觀念都建立起來之後,今天要正式進入 FastAPI 的核心機制。你會學到 FastAPI 怎麼用裝飾器組合複雜的 URL 模式、Pydantic v2 怎麼幫你設計嚴謹的資料模型、為什麼 FastAPI 能自動產生 Swagger UI 與 ReDoc 兩份互動式 API 說明頁面,最後用一個「書本管理 API」範例把所有東西串起來。這個範例會是後續每天演進的基礎。

引言

如果把寫後端比喻成蓋房子,前三天做的是看地圖(系列導覽)、準備工具(環境與工具鏈)、理解建築法規(HTTP 與 REST)。今天開始要真正搬磚了——把第一面牆砌起來。FastAPI 之所以在 2018 年推出後迅速成為 Python 後端的主流框架,核心原因有三:基於 Python 型別提示(type hints)做自動驗證、效能接近 Node.js 與 Go、生態系(特別是 Pydantic v2)完整。它的設計哲學是「讓型別提示就是規格、讓規格就是測試」,所以工程師寫一次程式就能拿到正確的請求驗證、自動產生的 OpenAPI 規格、以及互動式 API 說明頁面。

這篇文章會帶你深入 FastAPI 的三個核心:路由(routing)把 URL 對應到 Python 函式;Pydantic v2 模型(schema)描述請求與回應的形狀;自動說明頁面(automatic documentation)讓 Swagger UI 與 ReDoc 直接從程式碼生出來。今天的範例會做一個簡單的「書本管理 API」,支援建立、讀取、更新、刪除書本資料,並用型別提示做嚴格的請求驗證。學完之後,你就具備寫一個完整 CRUD 後端的能力。

FastAPI 的核心概念

FastAPI 是站在兩個巨人肩上建起來的:Starlette(處理 HTTP 與 ASGI)與 Pydantic(處理資料驗證)。它的「最小核心」其實就是一個 FastAPI 物件加上若干路由函式:

# 最小的 FastAPI 程式
from fastapi import FastAPI

app = FastAPI()


@app.get("/")
def root():
    return {"message": "hello"}


# 啟動:uvicorn main:app --reload

FastAPI() 物件負責整個應用程式的設定、路由表、說明頁面生成;@app.get("/") 是裝飾器,把下面的函式註冊成「GET /」的處理器。FastAPI 用 Starlette 的路由系統,所以支援路徑參數、查詢參數、相依性注入、中介軟體(middleware)等所有標準功能。型別提示(例如 item_id: int)則會被 Pydantic 拿去做自動驗證。

FastAPI 的另一個強項是「說明頁面」。只要你在函式簽章用了型別提示,並用 Pydantic 模型描述請求與回應,FastAPI 就能從你的程式碼直接生出 OpenAPI 規格,然後渲染成兩種互動式說明頁面:Swagger UI(在 /docs)與 ReDoc(在 /redoc)。這對前後端協作是革命性的改進,前端工程師可以直接從說明頁面看到「每個端點要送什麼、回什麼」,不再需要翻後端程式碼或問後端工程師。

路由:路徑參數、查詢參數與請求主體

FastAPI 把 HTTP 請求的不同部分對應到 Python 函式的不同參數:

HTTP 部位 Python 參數 範例
路徑參數(path parameter) 函式參數 /items/{item_id} → item_id: int
查詢參數(query string) 有預設值的函式參數 /items?skip=0&limit=10 → skip: int = 0
請求主體(body) Pydantic 模型 POST /items + JSON → payload: ItemCreate
標頭(headers) 由 Header() 注入 x-token: str = Header(...)
Cookies 由 Cookie() 注入 session_id: str = Cookie(None)

FastAPI 靠「參數是否有型別提示」與「是否有預設值」來判斷這個參數屬於哪一類。如果參數是簡單型別(int、str、bool、uuid.UUID)且沒有預設值,就是路徑參數;如果有預設值,就是查詢參數(可選);如果是 Pydantic 模型,則是請求主體。這個推導機制讓函式簽章非常乾淨,不需要像 Flask 那樣到處寫 request.args.get(...)。

路徑參數可以做嚴格的型別驗證:

# 路徑參數自動驗證
from fastapi import FastAPI

app = FastAPI()


@app.get("/items/{item_id}")
def read_item(item_id: int):
    # 如果 item_id 不是整數(例如 /items/abc)會回 422
    return {"item_id": item_id * 2}


@app.get("/files/{file_path:path}")
def read_file(file_path: str):
    # 用 :path 修飾符可以讓斜線也包含在參數裡
    return {"file_path": file_path}


@app.get("/users/{user_id}")
def read_user(user_id: int, q: str | None = None):
    # user_id 是必填路徑參數;q 是選填查詢參數
    return {"user_id": user_id, "q": q}

第一個端點示範了型別驗證:如果你打 /items/abc,FastAPI 會自動回 422 並告訴你「item_id 應該是整數」,不需要你寫任何檢查程式。第二個端點示範了 :path 修飾符——預設路徑參數不會包含斜線,如果你想讓整段路徑都當作一個參數,要明確寫 :path。第三個端點展示了選填查詢參數 q: str | None = None。

Pydantic v2 資料模型

Pydantic v2 是 FastAPI 的靈魂。它讓你用 Python class 定義「資料長什麼樣」,Pydantic 自動處理驗證、序列化、與 JSON 轉換。FastAPI 預設用 Pydantic v2(2.x 版),跟 v1 的 API 差很多,這系列從一開始就用 v2 的寫法:

# Pydantic v2 的資料模型寫法
from datetime import datetime

from pydantic import BaseModel, EmailStr, Field


class User(BaseModel):
    # 必填欄位
    id: int
    username: str = Field(min_length=3, max_length=40)
    email: EmailStr
    # 選填欄位(有預設值)
    is_active: bool = True
    created_at: datetime | None = None


# 自動驗證
user = User(id=1, username="alice", email="alice@example.com")
print(user.username)
# 輸出:alice

# 不合法的資料會拋 ValidationError
try:
    User(id=2, username="al", email="not-an-email")
except Exception as e:
    print("驗證失敗:", type(e).__name__)
# 輸出:驗證失敗: ValidationError

Pydantic v2 的關鍵設計:欄位預設值是「型別提示的順序」,必填欄位放前面;用 Field(min_length=3) 等內建約束描述限制;用 EmailStr 等特殊型別做格式驗證;可以巢狀其他 Pydantic 模型;可以自訂驗證器(validator)。這套機制讓「資料形狀」和「程式邏輯」完全分離,改 API 規格只要改 Pydantic 模型,其他程式碼自動跟著更新。

API 設計通常會區分「請求模型」與「回應模型」。例如建立使用者時不帶 id(伺服器給)、更新時所有欄位都可選:

# 區分請求與回應模型
from pydantic import BaseModel, ConfigDict, Field


class UserBase(BaseModel):
    username: str = Field(min_length=3, max_length=40)
    email: str = Field(pattern=r"^[^@\s]+@[^@\s]+\.[^@\s]+$")


class UserCreate(UserBase):
    # 建立請求:帶密碼,不帶 id
    password: str = Field(min_length=8, max_length=128)


class UserUpdate(BaseModel):
    # 更新請求:所有欄位都可選
    username: str | None = Field(default=None, min_length=3, max_length=40)
    email: str | None = None
    is_active: bool | None = None


class UserPublic(UserBase):
    # 回應模型:帶 id 與時間戳,不帶密碼
    id: int
    is_active: bool = True

    model_config = ConfigDict(from_attributes=True)
    # from_attributes=True 讓 Pydantic 可以從 ORM 物件(Day 6)建立模型

這個寫法展現了 API 設計的兩個好習慣:第一,UserPublic 不包含密碼欄位,避免不小心把密碼雜湊洩漏到 API 回應;第二,UserUpdate 用 None 表達「不更新這個欄位」,讓 PATCH 端點可以只更新部分欄位。Day 5 會深入探討請求驗證的進階主題。

自動說明頁面:Swagger UI 與 ReDoc

FastAPI 自動產生兩種 API 說明頁面:Swagger UI(互動式,可直接從瀏覽器發請求)與 ReDoc(唯讀,規格導向)。這兩份頁面都從你寫的 Python 程式碼(型別提示、Pydantic 模型、docstring)生出來,所以永遠跟程式同步,不會有說明過時的問題。

讓說明頁面更實用的小技巧:

# 用 docstring 與 response_model 讓說明更清楚
from fastapi import FastAPI, status
from pydantic import BaseModel, Field

app = FastAPI(
    title="書本管理 API",
    description="練習 FastAPI 的書本 CRUD 範例",
    version="0.1.0",
    # 可以在 docs 頁加聯絡資訊、授權等 metadata
    contact={"name": "Hao", "url": "https://blog.hao-code.com/"},
    license_info={"name": "MIT"},
)


class Book(BaseModel):
    id: int
    title: str = Field(min_length=1, max_length=200)
    author: str = Field(min_length=1, max_length=80)
    year: int = Field(ge=0, le=2100)


class BookCreate(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    author: str = Field(min_length=1, max_length=80)
    year: int = Field(ge=0, le=2100)


@app.get(
    "/books/{book_id}",
    response_model=Book,
    summary="取得單本書",
    description="依據 ID 取得一本書的詳細資料。如果找不到會回 404。",
    responses={404: {"description": "找不到這本書"}},
)
def get_book(book_id: int):
    """取得單本書的詳細資料。

    Args:
        book_id: 書本的唯一識別碼。

    Returns:
        Book 物件。

    Raises:
        HTTPException: 找不到時回 404。
    """
    # ... 實作略
    return Book(id=book_id, title="placeholder", author="placeholder", year=2024)

summary、description、response_model、responses 這些參數會直接反映到 Swagger UI 上;docstring 也會被擷取。實務上建議每個端點都寫簡短的 summary 與 description,這對前後端溝通非常有幫助。response_model 除了影響說明頁面,還會被 FastAPI 用來「過濾回應」——例如 UserPublic 沒列的密碼欄位,即使你的函式不小心回傳了,也不會被送到呼叫端。這是一道重要的安全防線,Day 10 會再深入。

完整實作:書本管理 API

把今天所有東西一次做完。我們建立一個完整的書本 CRUD API,包含型別提示、Pydantic 模型、查詢參數篩選、自動說明、最後用 httpx 走完整個生命週期:

建立 src/book_api/main.py:

# src/book_api/main.py
# 書本管理 API(FastAPI + Pydantic v2)
from typing import Annotated

from fastapi import FastAPI, HTTPException, Query, status
from pydantic import BaseModel, ConfigDict, Field

app = FastAPI(
    title="書本管理 API",
    description="練習 FastAPI 的書本 CRUD 範例",
    version="0.1.0",
)


# ---------- 資料模型 ----------
class BookBase(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    author: str = Field(min_length=1, max_length=80)
    year: int = Field(ge=0, le=2100)


class BookCreate(BookBase):
    # 建立用:繼承 BookBase,沒有額外欄位
    pass


class BookUpdate(BaseModel):
    # 更新用:所有欄位都可選
    title: str | None = Field(default=None, min_length=1, max_length=200)
    author: str | None = Field(default=None, min_length=1, max_length=80)
    year: int | None = Field(default=None, ge=0, le=2100)


class Book(BookBase):
    id: int
    model_config = ConfigDict(from_attributes=True)


# ---------- 假資料(Day 6 會換成 SQLite) ----------
_books: dict[int, Book] = {
    1: Book(id=1, title="Python 程式設計實戰", author="Hao", year=2024),
    2: Book(id=2, title="FastAPI 入門", author="Wei", year=2025),
}
_next_id = 3


# ---------- 端點 ----------
@app.get("/books", response_model=list[Book], summary="列出書本")
def list_books(
    author: Annotated[str | None, Query(description="依作者篩選")] = None,
    limit: Annotated[int, Query(ge=1, le=100)] = 10,
):
    items = list(_books.values())
    if author is not None:
        items = [b for b in items if b.author == author]
    return items[:limit]


@app.get("/books/{book_id}", response_model=Book, summary="取得單本書")
def get_book(book_id: int):
    book = _books.get(book_id)
    if book is None:
        raise HTTPException(status_code=404, detail="book not found")
    return book


@app.post(
    "/books",
    response_model=Book,
    status_code=status.HTTP_201_CREATED,
    summary="建立新書",
)
def create_book(payload: BookCreate):
    global _next_id
    new = Book(id=_next_id, **payload.model_dump())
    _books[_next_id] = new
    _next_id += 1
    return new


@app.patch("/books/{book_id}", response_model=Book, summary="更新書本")
def update_book(book_id: int, payload: BookUpdate):
    book = _books.get(book_id)
    if book is None:
        raise HTTPException(status_code=404, detail="book not found")
    # 只更新使用者提供的欄位
    data = book.model_dump()
    for key, value in payload.model_dump(exclude_unset=True).items():
        data[key] = value
    updated = Book(**data)
    _books[book_id] = updated
    return updated


@app.delete(
    "/books/{book_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="刪除書本",
)
def delete_book(book_id: int):
    if book_id not in _books:
        raise HTTPException(status_code=404, detail="book not found")
    del _books[book_id]
    return None


# 啟動:uvicorn book_api.main:app --reload --port 8000

這個範例做了幾件事:

  • 用 Annotated[type, Query(...)] 寫查詢參數,這是 FastAPI 0.95+ 推薦的新寫法,型別與驗證參數分開,比 name: int = Query(1, ge=1) 更清楚。
  • PATCH 用 exclude_unset=True 只取使用者真的有給的欄位,避免把選填欄位用 None 覆蓋掉原本的值。
  • status.HTTP_201_CREATED、status.HTTP_204_NO_CONTENT 這些常數比直接寫 201、204 更易讀。
  • response_model 明確指定回應模型,讓 Swagger UI 與自動說明頁面都能正確顯示回應結構。

啟動並用 httpx 走完整個 CRUD:

# tests/test_book_cycle.py
import httpx

BASE = "http://127.0.0.1:8000"


def main():
    with httpx.Client(base_url=BASE, timeout=5.0) as client:
        # 列出所有書
        r = client.get("/books")
        print("GET /books ->", r.status_code, r.json())
        # 輸出(範例):GET /books -> 200 [{'id': 1, ...}, {'id': 2, ...}]

        # 篩選 + 限制筆數
        r = client.get("/books", params={"author": "Hao", "limit": 5})
        print("GET /books?author=Hao ->", r.status_code, r.json())
        # 輸出(範例):GET /books?author=Hao -> 200 [{'id': 1, 'title': 'Python 程式設計實戰', ...}]

        # 建立新書
        new = {"title": "資料庫設計", "author": "Hao", "year": 2025}
        r = client.post("/books", json=new)
        print("POST /books ->", r.status_code, r.json())
        # 輸出(範例):POST /books -> 201 {'id': 3, 'title': '資料庫設計', 'author': 'Hao', 'year': 2025}
        book_id = r.json()["id"]

        # 部分更新(只改 year)
        r = client.patch(f"/books/{book_id}", json={"year": 2026})
        print(f"PATCH /books/{book_id} ->", r.status_code, r.json())
        # 輸出(範例):PATCH /books/3 -> 200 {'id': 3, 'title': '資料庫設計', 'author': 'Hao', 'year': 2026}

        # 驗證請求錯誤(year 太大)
        r = client.post("/books", json={"title": "x", "author": "y", "year": 9999})
        print("POST /books (year=9999) ->", r.status_code, r.json())
        # 輸出(節錄):POST /books (year=9999) -> 422 {'detail': [{'type': 'less_than_equal', 'loc': ['body', 'year'], ...}]}

        # 刪除
        r = client.delete(f"/books/{book_id}")
        print(f"DELETE /books/{book_id} ->", r.status_code)
        # 輸出(範例):DELETE /books/3 -> 204


main()

這個迴圈展示「正常流程 + 錯誤流程」兩條路線:建立、更新用 201、200,錯誤輸入用 422,刪除用 204,每個狀態碼都有對應的語意。實務上你會把這些測試包成 test_collection,並用 FastAPI 的 TestClient(Day 16)做更系統化的測試。

常見錯誤與踩雷

第一個常見的踩雷是「Pydantic v1 vs v2 寫法混用」。在 2024 年之前很多教學用的是 Pydantic v1 的 @validator、Config inner class;v2 改成 @field_validator、model_config = ConfigDict(...)。兩者不相容,直接複製舊教學會讓 FastAPI 報錯。如果你看到錯誤訊息提到 validator、Config,先確認你用的是哪一版。本系列固定使用 Pydantic v2,規格書也以 2.11 為基準。

第二個是「忘了裝 Annotated」。FastAPI 0.95 之後推薦用 Annotated[type, Query()] 寫查詢參數;但有些舊教學仍寫 name: int = Query(1),這在新版也還能用,但失去了型別與驗證分離的好處。建議一律用 Annotated 寫法,說明頁面更乾淨。

第三個是「response_model 與回傳值型別衝突時 FastAPI 會自動過濾」。如果你的函式回傳了一個 User 物件(含密碼雜湊)但 response_model=UserPublic,FastAPI 會把密碼欄位過濾掉。這通常是好事,但偶爾會讓除錯變困難:你想看密碼欄位卻看不到。除錯時可以暫時把 response_model 拿掉,確認完整資料是否真的有送出來。

效能與實務提醒

FastAPI 的效能優勢來自 Starlette(ASGI 非同步)與 Pydantic v2(用 Rust 寫的驗證核心)。在本機簡單測試下,FastAPI 0.116 的吞吐量比 Flask 高三到五倍,比 Django REST Framework 高十倍以上。但這些數字都是「Hello World 等級」的對比,實務上的瓶頸通常在資料庫查詢、I/O 與第三方 API 呼叫,框架本身的效能差異往往只佔 10% 以下。Day 18 會示範怎麼用非同步 I/O 把 I/O 密集型 API 的吞吐量再拉上去。

response_model 的過濾是有代價的。當 response_model 是複雜的巢狀結構,Pydantic 會做一次驗證與序列化;對於非常大的回應(例如一次回傳一萬筆資料),這個步驟會佔用可觀的 CPU。這種情境可以考慮改成「stream 回應」或「分頁」(Day 7 會詳細講)。

Swagger UI 在開發階段非常好用,但正式環境通常不希望把整份 API 暴露在外。可以透過環境變數(Day 29)動態決定是否掛載 /docs:

# 依環境變數決定是否顯示說明頁面
from fastapi import FastAPI
import os

app = FastAPI(docs=None if os.getenv("ENV") == "production" else "/docs")
# 上線時設定 ENV=production 就會關閉 /docs

小結

今天我們正式進入 FastAPI 的核心:路由用裝飾器組合 URL 與 HTTP 方法、Pydantic v2 模型用型別提示與 Field 約束做嚴格驗證、response_model 與自動參數描述讓 Swagger UI 與 ReDoc 從程式碼生出來。我們做了一個完整的「書本管理 API」,支援串列、單筆讀取、建立、部分更新、刪除五個端點,並展示錯誤輸入會自動回 422 的標準行為。這些機制會在後續每天反覆出現,是整個系列最常被用到的 FastAPI 子集合。

進階範例:用 Pydantic 做複雜的巢狀模型

真實世界的 API 經常需要處理巢狀的資料結構:例如「訂單裡有多個訂單細項,每個細項裡又有商品資訊」。Pydantic v2 把這件事做得非常自然——只要把其他 Pydantic 模型當作欄位型別,就能自動遞迴驗證與序列化。

# nested_models.py
# Pydantic v2 的巢狀模型示範
from pydantic import BaseModel, Field


class Author(BaseModel):
    name: str = Field(min_length=1, max_length=80)
    email: str = Field(pattern=r"^[^@\s]+@[^@\s]+\.[^@\s]+$")


class Book(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    author: Author  # 巢狀:直接用另一個 Pydantic 模型當欄位型別
    year: int = Field(ge=0, le=2100)


# 建立並驗證
book = Book(
    title="Python 程式設計實戰",
    author=Author(name="Hao", email="hao@example.com"),
    year=2024,
)
print(book.model_dump())
# 輸出(範例):
# {
#     'title': 'Python 程式設計實戰',
#     'author': {'name': 'Hao', 'email': 'hao@example.com'},
#     'year': 2024
# }

# 巢狀驗證失敗會傳到正確的欄位
try:
    Book(
        title="壞的書",
        author=Author(name="", email="not-email"),  # name 太短、email 格式錯
        year=2024,
    )
except Exception as e:
    print("驗證失敗:", type(e).__name__)
    # 輸出:驗證失敗: ValidationError

這支範例展示 Pydantic 巢狀模型的兩個重點:第一,Author 是一個獨立的 Pydantic class,可以單獨使用(例如「只回傳作者資料」的端點);第二,巢狀驗證會把所有錯誤一次列出,包括 author.name 太短與 author.email 格式不對。這對 API 的「錯誤回應結構化」設計非常有幫助——前端可以直接對應到表單欄位,不需自己判斷哪裡錯了。

第二個進階主題是「用 Pydantic 做設定管理」。後端專案通常需要讀一堆設定(資料庫連線、API key、功能開關),把這些設定寫在環境變數或設定檔後,讓 Pydantic 在啟動時一次驗證所有值是合法、是極佳的設計:

# settings.py
# 用 Pydantic BaseSettings 管理後端設定(pydantic-settings 套件)
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class AppSettings(BaseSettings):
    # 從環境變數讀取,預設值是開發用
    app_name: str = "book-api"
    debug: bool = False
    database_url: str = Field(default="sqlite:///./books.db")
    api_key: str | None = Field(default=None, description="外部 API 金鑰")
    max_connections: int = Field(default=10, ge=1, le=100)

    model_config = SettingsConfigDict(env_file=".env", env_prefix="BOOK_")
    # 會從 BOOK_APP_NAME、BOOK_DEBUG 這些環境變數讀取
    # 也可以寫在 .env 檔裡


# 用法
settings = AppSettings()
print(f"應用名稱:{settings.app_name}")
print(f"除錯模式:{settings.debug}")
print(f"資料庫 URL:{settings.database_url}")
# 輸出(範例):
#   應用名稱:book-api
#   除錯模式:False
#   資料庫 URL:sqlite:///./books.db

pydantic-settings 是 Pydantic 團隊推出的設定管理套件,把「從環境變數讀取 + 用 Pydantic 驗證格式」打包在一起。env_prefix="BOOK_" 讓所有設定都加上前綴,避免與其他系統的環境變數衝突;env_file=".env" 則讓你可以把開發用的設定寫在 .env 裡(記得放進 .gitignore!)。這個寫法在 Day 29 會正式介紹,今天先預習。

第三個進階設計是「用 Enum 管理允許值」。如果某個欄位只能接受特定的幾個值(例如書本的「類型」只能是「電子書、紙本書、有聲書」),用 Pydantic 的 Enum 比字串比對更安全。

# enum_models.py
# Pydantic 與 Enum 的搭配
from enum import Enum

from pydantic import BaseModel, Field


class BookFormat(str, Enum):
    PAPERBACK = "paperback"  # 平裝
    HARDCOVER = "hardcover"  # 精裝
    EBOOK = "ebook"          # 電子書
    AUDIOBOOK = "audiobook"  # 有聲書


class BookV2(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    format: BookFormat = BookFormat.PAPERBACK


# 合法
b = BookV2(title="範例書", format=BookFormat.EBOOK)
print(f"{b.title} ({b.format.value})")
# 輸出:範例書 (ebook)

# 非法值會被拒絕
try:
    BookV2(title="壞的書", format="video")
except Exception as e:
    print("驗證失敗:", type(e).__name__)
    # 輸出:驗證失敗: ValidationError

用 str, Enum 而不是純 Enum,可以讓這個 enum 同時當字串使用(直接序列化為 JSON)又保留 enum 的型別安全。當你的 API 有「狀態」、「類型」、「等級」這類受限制的欄位時,這個設計讓前後端都有清楚的對應。

結語

今天我們從「Hello World 等級的 FastAPI」走到「完整 CRUD 的書本管理 API」,但目前的資料是存在記憶體裡的 _books 字典,伺服器重啟就會消失。明天,我們會進入更實用的機制:請求驗證(涵蓋路徑參數、查詢參數、請求主體、標頭的綜合驗證)與相依性注入(FastAPI 最強大的設計之一)。你會學到怎麼把常用邏輯抽出來、用 Depends 自動注入,以及怎麼設計一個可維護的後端架構。

延伸資源

  • FastAPI 官方教學(2025):https://fastapi.tiangolo.com/tutorial/
  • Pydantic v2 遷移指南(2025):https://docs.pydantic.dev/latest/migration/
  • OpenAPI 3.1 規格書(2023):https://spec.openapis.org/oas/v3.1.0
  • Starlette 官方介紹(2025):https://www.starlette.io/
  • Annotated 型別(PEP 593,2019):https://peps.python.org/pep-0593/
  • FastAPI path operation configuration(2025):https://fastapi.tiangolo.com/tutorial/path-operation-configuration/

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...