跳到主要內容

FE Day 15 測試:Vitest 與元件測試

FE Day 15 測試:Vitest 與元件測試

執行需求:CPU 可跑。今天是系列的第十五篇。前十四天我們從環境、TypeScript、React 元件、Hooks、Context、表單到 TanStack Query 一路往上疊,寫了不少「能跑」的程式碼。但程式碼能跑跟「改完不會壞掉」是兩件事——後者需要測試。今天要介紹前端測試的事實標準——Vitest 3.x 與 React Testing Library,把前幾天寫的 Hook、表單、API 呼叫全部加上單元測試。我們會從最基本的「斷言一個數字」開始,逐步演練到「測試自訂 Hook」、「測試表單送出」、「測試 TanStack Query 元件」、「mock fetch」。整篇範例都在本機 CPU 跑得起來,不依賴雲端服務,跑完你會拿到一份可以直接 pnpm test 跑的測試套件。

引言

寫後端的我們對測試並不陌生:pytest、unittest、fixture、mock、coverage——這些是後端的基本功。前端的測試觀念類似,但工具有差異:Vitest 是「Vite 生態的測試執行器」,速度比 Jest 快很多、與 Vite 設定共用;React Testing Library(RTL)是「以使用者觀點測試元件」的函式庫,鼓勵你測「使用者會做什麼」而不是「元件內部狀態」。兩者結合起來就是 React 專案最常見的測試組合。

前端測試的價值不只是「驗證邏輯正確」,更重要的是「讓改 code 變得安全」。當元件長大、邏輯變複雜,一個小修改可能影響到整個應用。有測試的話,改壞了 CI 會跳出來擋下;沒測試的話,使用者會跳出來擋你。前端測試還有一個獨特價值:能在「不啟動瀏覽器」的情況下驗證元件,這對 CI 環境特別有用。

今天要回答五個問題:第一,Vitest 3.x 怎麼設定?第二,怎麼用 React Testing Library 測元件?第三,怎麼用 renderHook 測自訂 Hook?第四,怎麼 mock fetch 與 localStorage?第五,怎麼測 TanStack Query 元件?我們會給 useToggle、useDebounce、BookingForm、BookingList 寫完整測試。整篇閱讀時間約 35 分鐘,動手做大約 45 分鐘。

安裝 Vitest 與 Testing Library

在既有 Vite 專案裝測試套件:

# 裝 Vitest 3.x 與相關套件
pnpm add -D vitest@^3 @vitest/coverage-v8 jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event

幾個套件的角色分工:vitest 是測試執行器;jsdom 提供瀏覽器環境模擬(讓 document、window 可用);@testing-library/react 提供 render、screen 等工具;@testing-library/jest-dom 提供 toBeInTheDocument 等自訂斷言;@testing-library/user-event 模擬真實使用者操作(比 fireEvent 更貼近真實)。

Vitest 設定檔

Vitest 可以直接讀 Vite 的設定,但測試需要 jsdom 環境,所以額外寫一份設定:

// vitest.config.ts
// Vitest 設定:沿用 Vite 設定,但測試環境改用 jsdom
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom", // 瀏覽器環境(讓 window、document 可用)
    globals: true,        // 啟用 describe / it / expect 等全域 API
    setupFiles: ["./src/test/setup.ts"], // 共用設定:引入 jest-dom 等
    css: false,           // 測試不處理 CSS(避免 Tailwind className 報錯)
  },
});

這份設定把 Vite 的 React plugin 帶進來(讓測試能處理 .tsx)、測試環境換成 jsdom、並引入一份共用的 setup 檔。

// src/test/setup.ts
// 測試共用設定:引入 jest-dom 的自訂斷言
import "@testing-library/jest-dom/vitest";

setup 檔的內容很簡單——只是引入 jest-dom 提供的自訂斷言。這樣在測試裡就能用 expect(button).toBeInTheDocument() 而不是 expect(button).toBeTruthy(),語意更清楚。

第一個測試:斷言一個純函式

先從最簡單的純函式測試開始,建立對 Vitest 的基本印象:

// src/utils/format.test.ts
// 測試一個簡單的格式化函式
import { describe, it, expect } from "vitest";
import { formatPrice } from "./format";

describe("formatPrice", () => {
  it("把數字格式化為千分位 + NT$ 前綴", () => {
    expect(formatPrice(1234)).toBe("NT$1,234");
  });

  it("處理零", () => {
    expect(formatPrice(0)).toBe("NT$0");
  });

  it("處理負數", () => {
    expect(formatPrice(-500)).toBe("NT$-500");
  });
});

Vitest 的核心 API 與 Jest 幾乎相同:describe 把測試分群、it 定義單一測試、expect 做斷言。常見的 matcher 有 toBe(嚴格相等)、toEqual(深層相等)、toContain(陣列/字串包含)、toBeNull、toBeUndefined、toBeTruthy、toThrow(拋出例外)等。

// src/utils/format.ts
// 對應的實作
export function formatPrice(n: number): string {
  return `NT$${n.toLocaleString("en-US")}`;
}

執行測試:pnpm vitest run。Vitest 會列出所有測試檔、跑過每一個 it 區塊、最後印出通過/失敗統計。預設 Vitest 進入 watch 模式(檔案變動自動重跑),加 run 參數才跑一次後退出,適合 CI 使用。

測試元件:用 React Testing Library

React Testing Library 的核心是 render:把元件渲染到 jsdom 裡、回傳 DOM 與工具函式。最基本的測試是「確認元件有渲染某段文字」。

// src/components/Greeting.test.tsx
// 測試一個最簡單的元件
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { Greeting } from "./Greeting";

describe("Greeting", () => {
  it("顯示傳入的姓名", () => {
    render(<Greeting name="小明" />);
    // getByText:找出內容完全相符的元素,找不到會拋錯
    expect(screen.getByText("Hello, 小明")).toBeInTheDocument();
  });
});

render 把元件掛到 jsdom 的 document.body 裡;screen 提供查詢 DOM 的函式(getByText、getByRole、getByLabelText 等)。getByText 找不到會拋錯(測試失敗),queryByText 找不到會回傳 null(測試可以繼續)。React Testing Library 的設計哲學是「測試使用者會看到的東西」——使用者看到的不是 class、不是 data-testid,而是文字、按鈕、標籤。所以優先用 getByRole、getByLabelText,data-testid 留到最後才用。

下面是測試「使用者互動」的範例:

// src/components/Counter.test.tsx
// 測試一個計數器元件:點按鈕會加 1
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Counter } from "./Counter";

describe("Counter", () => {
  it("點按鈕會讓數字加 1", async () => {
    const user = userEvent.setup();
    render(<Counter />);

    // 初始數字是 0
    expect(screen.getByText("目前:0")).toBeInTheDocument();

    // 模擬使用者點按鈕
    await user.click(screen.getByRole("button", { name: "+1" }));

    // 數字變成 1
    expect(screen.getByText("目前:1")).toBeInTheDocument();
  });
});

userEvent.setup() 建立一個模擬使用者物件;user.click(element) 模擬點擊。userEvent 比 fireEvent 更貼近真實——它會依序觸發 pointerdown、pointerup、click 等事件,而不是只觸發 click。async/await 是必要的,因為 userEvent 的 API 是 async(要等待瀏覽器內部事件流程模擬)。

測試自訂 Hook:renderHook

Day 10 教的自訂 Hook 怎麼測?React Testing Library 提供 renderHook,把 Hook 渲染到測試元件裡、回傳目前的值與 rerender 函式。

// src/hooks/useToggle.test.ts
// 測試 useToggle:呼叫 toggle 後 value 會反過來
import { describe, it, expect } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { useToggle } from "./useToggle";

describe("useToggle", () => {
  it("預設值是 false", () => {
    const { result } = renderHook(() => useToggle());
    expect(result.current[0]).toBe(false);
  });

  it("呼叫 toggle 後值反過來", () => {
    const { result } = renderHook(() => useToggle());

    act(() => {
      result.current[1](); // 呼叫 toggle
    });

    expect(result.current[0]).toBe(true);
  });

  it("呼叫 setValue 可以強制設值", () => {
    const { result } = renderHook(() => useToggle(false));

    act(() => {
      result.current[2](true); // setValue(true)
    });

    expect(result.current[0]).toBe(true);
  });
});

renderHook 回傳的 result.current 是 Hook 回傳的最新值(在這個範例是 [value, toggle, setValue] 元組)。act 用來包裝「會觸發 React 狀態更新」的動作,確保所有更新都被 React 處理完畢後再繼續斷言。沒有用 act 包裝可能會出現「狀態沒更新就斷言」的競態問題。

測試 useDebounce 需要 fake timers:

// src/hooks/useDebounce.test.ts
// 測試 useDebounce:300ms 後才更新值
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { useDebounce } from "./useDebounce";

describe("useDebounce", () => {
  beforeEach(() => vi.useFakeTimers()); // 啟用 fake timers
  afterEach(() => vi.useRealTimers());   // 測試完恢復真實 timers

  it("300ms 後才更新值", () => {
    const { result, rerender } = renderHook(({ value }) => useDebounce(value, 300), {
      initialProps: { value: "hello" },
    });
    expect(result.current).toBe("hello");

    // 改 value,hook 還沒更新
    rerender({ value: "world" });
    expect(result.current).toBe("hello");

    // 快轉 300ms
    act(() => {
      vi.advanceTimersByTime(300);
    });

    expect(result.current).toBe("world");
  });
});

Fake timers 讓我們可以「快轉時間」而不真的等 300 毫秒,這對測試任何有 setTimeout、setInterval 的程式碼非常有用。vi.advanceTimersByTime(300) 把時間快轉 300 毫秒,所有在這段時間內應該被觸發的 callback 都會立刻執行。

Mock fetch:測試 API 呼叫

測試 API 呼叫時不能真的打 HTTP——一是會依賴網路、二是會打到真實伺服器。Vitest 提供 vi.spyOn(global, "fetch") 或直接替換 globalThis.fetch 來 mock。

// src/hooks/useBookings.test.tsx
// 測試 useBookings:mock fetch 並用 QueryClientProvider 包起來
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useBookings } from "./useBookings";

// 建立一個全新的 QueryClient(避免測試之間共用快取)
function createWrapper() {
  const client = new QueryClient({
    defaultOptions: { queries: { retry: false } }, // 測試不要重試,失敗就失敗
  });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={client}>{children}</QueryClientProvider>
  );
}

describe("useBookings", () => {
  beforeEach(() => {
    vi.restoreAllMocks(); // 每個測試前清掉 mock
  });

  it("成功抓取預約清單", async () => {
    const mockData = [
      { id: "1", name: "王小明", date: "2026-04-01", time: "morning" as const },
    ];
    vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
      ok: true,
      json: async () => mockData,
    }));

    const { result } = renderHook(() => useBookings(), { wrapper: createWrapper() });

    await waitFor(() => {
      expect(result.current.isSuccess).toBe(true);
    });

    expect(result.current.data).toEqual(mockData);
  });

  it("API 失敗時回傳錯誤", async () => {
    vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
      ok: false,
      status: 500,
      json: async () => ({}),
    }));

    const { result } = renderHook(() => useBookings(), { wrapper: createWrapper() });

    await waitFor(() => {
      expect(result.current.isError).toBe(true);
    });

    expect(result.current.error?.message).toContain("HTTP 500");
  });
});

這個測試展示了測試 TanStack Query 的標準模式:用 QueryClientProvider 包起來(透過 wrapper 選項)、vi.stubGlobal("fetch", ...) 替換全域 fetch、waitFor 等非同步操作完成。retry: false 是關鍵——測試裡不要讓 TanStack Query 自動重試,否則一個失敗的測試會拖很久才結束。

測試表單:模擬使用者輸入

Day 12 的 BookingFormRHF 怎麼測?重點是「模擬使用者輸入、觸發驗證、呼叫 API」。表單元件的測試有三個關鍵場景:欄位未填時顯示驗證錯誤、欄位填好後正確呼叫 onSubmit、API 失敗時保留輸入並顯示錯誤訊息。下面是前兩個的測試範例,第三個會留給讀者當練習。

// src/components/BookingFormRHF.test.tsx
// 測試 React Hook Form 表單:輸入、驗證、送出
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { BookingFormRHF } from "./BookingFormRHF";

describe("BookingFormRHF", () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  it("欄位未填時送出會顯示驗證錯誤", async () => {
    const user = userEvent.setup();
    const onSubmit = vi.fn();
    render(<BookingFormRHF onSubmit={onSubmit} />);

    // 直接按送出,所有欄位都是空的
    await user.click(screen.getByRole("button", { name: "建立預約" }));

    // 驗證錯誤應該出現
    await waitFor(() => {
      expect(screen.getByText("姓名至少 2 個字")).toBeInTheDocument();
    });
    expect(onSubmit).not.toHaveBeenCalled();
  });

  it("欄位填好後送出會呼叫 onSubmit", async () => {
    const user = userEvent.setup();
    const onSubmit = vi.fn().mockResolvedValue(undefined);
    render(<BookingFormRHF onSubmit={onSubmit} />);

    // 填欄位
    await user.type(screen.getByLabelText("姓名"), "王小明");
    await user.type(screen.getByLabelText("Email"), "wang@example.com");
    await user.type(screen.getByLabelText("日期"), "2026-04-01");
    await user.selectOptions(screen.getByLabelText("時段"), "morning");

    // 送出
    await user.click(screen.getByRole("button", { name: "建立預約" }));

    await waitFor(() => {
      expect(onSubmit).toHaveBeenCalledWith({
        name: "王小明",
        email: "wang@example.com",
        date: "2026-04-01",
        time: "morning",
        note: "",
      });
    });
  });
});

這個測試展示三個重要技巧:第一,getByLabelText("姓名") 用 label 文字找到 input,這也是使用者實際操作的方式;第二,user.type 模擬「一字一字輸入」,會依序觸發 keydown、keypress、input 等事件;第三,waitFor 包裝斷言,會不斷重試直到斷言通過或超時,適合處理「送出後需要時間處理」的非同步情境。

常見錯誤與踩雷

第一個踩雷是「忘記用 act 包裝狀態更新」。act 確保 React 把所有更新處理完才繼續。如果你看到「not wrapped in act」警告,就是在提醒你「這個狀態更新沒被 act 包起來」。修法是用 act(() => { ... }) 或呼叫 async 版本的 act(async () => { ... })。在 React Testing Library 裡,render、userEvent、fireEvent 等函式內部已經呼叫了 act,所以「從測試直接呼叫 setState」才需要手動包。

第二個踩雷之前提過,再展開講:「測試之間共用 QueryClient」。如果你在 describe 外建立 QueryClient,所有測試會共用同一份快取,導致測試順序影響結果。例如 test A 抓了 bookings,test B 預期 bookings 是空陣列但實際拿到 test A 的資料。修法是用工廠函式(createWrapper())在每個測試建立新的 client,並把 retry 設為 false。這是測試隔離的基本紀律。

第三個踩雷之前也提過:「忘記清掉 mock」。vi.fn() 與 vi.stubGlobal 會污染全域狀態,沒清掉的話下一個測試會用到舊的 mock。修法是在 beforeEach 呼叫 vi.restoreAllMocks() 與 vi.unstubAllGlobals()。如果用 MSW(Mock Service Worker)模擬 HTTP 層,要在 afterEach 呼叫 server.resetHandlers() 重置 handler 狀態。

第四個踩雷之前也提過:「waitFor 沒給 expect」。waitFor 內部必須有 expect,否則它不知道什麼時候該停止。waitFor 預設 timeout 是 1000 毫秒,超過就拋錯。實務上如果你看到「waitFor timed out」訊息,通常是斷言條件永遠不會成立——可能是 selector 寫錯、可能是元件根本沒渲染那段文字、可能是 mock 沒生效。

第五個踩雷是「用 data-testid 太多」。React Testing Library 鼓勵「用使用者看得到的屬性找元素」,過度依賴 data-testid 會讓測試跟實作綁太緊。例如你把按鈕的 className 從 btn 改成 button-primary,用 getByRole 的測試不會壞,但用 getByTestId("submit-btn") 的測試會壞。優先順序:getByRole → getByLabelText → getByPlaceholderText → getByText → getByDisplayValue → getByAltText → getByTitle → getByTestId。data-testid 留到「真的沒有其他方法」才用。

第二個踩雷是「測試之間共用 QueryClient」。如果你在 describe 外建立 QueryClient,所有測試會共用同一份快取,導致測試順序影響結果。修法是用工廠函式(createWrapper())在每個測試建立新的 client。

第三個踩雷是「忘記清掉 mock」。vi.fn() 與 vi.stubGlobal 會污染全域狀態,沒清掉的話下一個測試會用到舊的 mock。修法是在 beforeEach 呼叫 vi.restoreAllMocks() 與 vi.unstubAllGlobals()。

第四個踩雷是「waitFor 沒給斷言而給副作用」。waitFor 內部必須有 expect,否則它不知道什麼時候該停止。例如下面寫法會無限迴圈:

// 反例:waitFor 內沒 expect,會無限等待
await waitFor(() => {
  console.log("loading…");
});

// 正例:waitFor 內有 expect,達到條件就停止
await waitFor(() => {
  expect(screen.getByText("完成")).toBeInTheDocument();
});

第五個踩雷是「用 data-testid 太多」。React Testing Library 鼓勵「用使用者看得到的屬性找元素」,過度依賴 data-testid 會讓測試跟實作綁太緊。優先順序:getByRole → getByLabelText → getByPlaceholderText → getByText → getByDisplayValue → getByAltText → getByTitle → getByTestId。

效能與實務提醒

Vitest 的執行速度是它最大的優勢。對中型專案(200+ 測試),Vitest 通常 5–10 秒跑完,Jest 需要 30–60 秒。原因是 Vitest 用 esbuild 做轉譯、平行跑測試、共用 Vite 的 module graph。如果你的測試跑得慢,先檢查是不是有「在 describe 頂層做昂貴的初始化」(應該放進 beforeEach 或工廠函式)。

另一個實務建議是「寫測試金字塔」。底層是大量單元測試(Hook、純函式),中層是少數元件測試(render + 互動),上層是少量 E2E 測試(Playwright,Day 27 會教)。單元測試跑得快、除錯容易;E2E 測試跑得慢、容易 flaky。比例建議:70% 單元、20% 元件、10% E2E。為什麼是這個比例?因為單元測試的「投資報酬率」最高:寫 1 小時能保護 100 個迴歸場景;E2E 測試則要花 3 小時保護 1 個場景。把資源集中在金字塔底層,長期的維護成本最低。

另一個實務提醒是「不要為了 100% 覆蓋率而硬寫測試」。Coverage 是一個指標,不是目標。如果某個元件只是

Hello
這種純展示,硬寫測試只會讓測試程式碼比元件還長。判斷原則:「這段程式碼有邏輯嗎?有,就該測」。純展示、無 props、無條件分支的元件可以跳過測試。

Coverage 是另一個常見指標。Vitest 內建 @vitest/coverage-v8,跑 pnpm vitest run --coverage 會產出覆蓋率報告。實務上不用追求 100%,重點是「核心邏輯(Hook、reducer、API 呼叫)覆蓋率 90% 以上、UI 元件 60% 以上」。對小工具函式與純元件可以容忍低覆蓋率。Vitest 也支援在 vitest.config.ts 設定 coverage.thresholds,如果某個檔案的覆蓋率低於門檻就讓測試失敗。建議把門檻設在「合理可達」的水位(例如 60%),不要設 100%,否則每加一個新元件就要補一堆測試。

最後是「測試程式碼也是程式碼」。測試檔應該跟正式程式碼一樣維護:避免重複(用 helper 函式)、保持可讀(斷言意圖清楚)、定期重構(測試壞掉時優先修測試而不是刪測試)。一個「寫了就忘」的測試比沒有測試更糟——它會讓你以為程式碼有保護,實際上保護已經失效。常見的測試重構模式:把多個測試共用的 setup 抽成 helper、把常見的斷言組合(例如「找到這個元素並檢查內容」)抽成自訂 matcher、把工廠函式(createBooking 等)集中在 src/test/factories.ts。

最後提一下測試與 CI 的整合。Day 2 我們把 verify 寫進 package.json,CI 上跑 pnpm verify 就會自動跑 lint + typecheck + test。Vitest 預設會在 CI 環境下用 run 模式(跑一次就退出),不會進入 watch。記得在 CI 設定裡加 CI=true 環境變數,Vitest 會自動調整行為(例如關掉顏色輸出)。

小結

今天我們把前端測試從「裝套件」到「寫 Hook 測試、表單測試、TanStack Query 元件測試」一次走完。重點回顧:Vitest 3.x 是 Vite 生態的測試執行器,速度比 Jest 快很多;React Testing Library 用「以使用者觀點測試」的哲學,鼓勵用 getByRole、getByLabelText 而不是 data-testid;renderHook 可以單獨測試自訂 Hook;mock fetch 用 vi.stubGlobal;測試 TanStack Query 要用 QueryClientProvider 包起來並關閉 retry。明天 Day 15 會進入專案結構主題——怎麼組織 src/、怎麼分層、怎麼避免「所有東西都塞在一個資料夾」的窘境,這是「多人協作中型 React 專案」的必修課。

結語

明天,我們會把焦點從「單一檔案」轉到「整個專案的組織」。Day 15 會介紹幾種常見的 React 專案結構(atomic design、feature-based、layer-based),並用「預約管理系統」當範例,示範怎麼從零開始規劃 src/ 下面的目錄結構。我們也會討論「什麼時候該拆資料夾」、「什麼時候該合併」、「共用元件 vs 業務元件怎麼分」。記得今天寫的所有測試先留著,明天會把它們組織到 src/components/__tests__/ 與 src/hooks/__tests__/ 對應的位置。

今天我們走過的測試 API 是你未來兩個月寫測試時的日常工具。即使你還沒記住所有細節,只要記住三個核心觀念就夠了:第一,「以使用者觀點測試」用 getByRole 與 getByLabelText 找元素;第二,「用 renderHook 測 Hook」、「用 render 測元件」、「用 waitFor 等非同步」;第三,「mock 外部依賴(fetch、storage、計時器)讓測試獨立」。這三個觀念涵蓋了 90% 的 React 元件測試需求,剩下的 10%(例如視覺測試、a11y 測試、E2E)會在後續章節陸續介紹。

延伸資源

  • Vitest 官方文件(3.x,2026 年 3 月):https://vitest.dev/
  • React Testing Library 官方文件:https://testing-library.com/docs/react-testing-library/intro/
  • Testing Library 〈Guiding Principles〉(為什麼推薦 getByRole):https://testing-library.com/docs/guiding-principles/
  • TanStack Query 測試指南:https://tanstack.com/query/latest/docs/framework/react/guides/testing
  • Kent C. Dodds〈Testing Implementation Details〉:https://kentcdodds.com/blog/testing-implementation-details

留言

這個網誌中的熱門文章

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