跳到主要內容

FE Day 9 useEffect:什麼時候該用、什麼時候不該

FE Day 9 useEffect:什麼時候該用、什麼時候不該

執行需求:CPU 可跑。今天是「前端開發實戰:React 與 Next.js 全套」系列的第九篇,正式進入「核心」章節。前八天你學會了元件、props、state、樣式,寫一個按鈕切換數字的小介面已經不成問題;但當畫面開始「讀瀏覽器 API」、「打 API 拿資料」、「訂閱某個事件」、「跟計時器連動」時,你會需要一個「副作用」機制——這就是 useEffect 的工作。useEffect 是 React Hooks 裡最容易「寫得動但寫錯」的一個,本篇要把它從頭到尾講清楚:什麼是副作用、什麼時候該用、什麼時候根本不該用、依賴陣列怎麼處理、cleanup 是什麼、以及為什麼「它不是生命週期方法」。整篇範例都在本機 CPU 跑得起來,不依賴雲端服務。

引言

寫後端的我們對「副作用」並不陌生:一個函式除了回傳值,還做了「改變外界狀態」的事情(例如寫檔、發 HTTP、寫 log),就被稱為有副作用。前端的副作用範圍更廣:直接操作 DOM、呼叫 fetch、訂閱瀏覽器事件、啟動計時器、把資料寫進 localStorage 全部都是。React 函式元件的渲染函式本身應該是純函式(純函式在 Day 5 已經介紹過),所以 React 提供一個專門的「出口」讓你做這些事:useEffect。

useEffect 在 React 社群裡有兩個極端評價:新手把它當萬靈丹,什麼都塞進去;老手則對它敬而遠之,能不用就不用。React 官方文件在 19.x 版後更明確地說:「useEffect 不是生命週期方法,它是把某些事情『同步到某個外部系統』用的機制。」這句話翻成白話是:「如果你做的不是『讓 React 之外的系統跟著元件狀態走』,那 useEffect 就不該出現」。

今天要解開五個常見疑問:第一,什麼是副作用?第二,useEffect 的 callback 什麼時候執行?第三,依賴陣列(dependency array)的規則是什麼?第四,cleanup 是什麼、為什麼一定要寫?第五,什麼情境根本「不該」用 useEffect?最後會用一個完整的「自動儲存到 localStorage 的筆記元件」當範例,把上述五點一次演練完。讀完這篇,你應該能在三秒內判斷某段程式碼「該不該放進 useEffect」,並能正確寫出 cleanup。

副作用與 useEffect 的本質

在 React 函式元件裡,元件函式(render function)每次渲染都會被執行一次。它應該只做一件事:根據 props 與 state 計算出要顯示的 JSX。任何「讀取瀏覽器 API」、「打 API」、「改變非 React 管理的狀態」、「訂閱事件」都不該直接寫在 render 函式裡,否則會造成兩個後果:第一,渲染結果不純,每次跑出來可能不一樣;第二,這些副作用會在每次渲染時都被執行一次,造成重複訂閱、重複請求、重複計時等 bug。

useEffect 就是 React 幫你開的一個「出口」,讓你在 render 之後(或元件被移除之前)做一些「跟外部世界同步」的事情。寫法很簡單:傳一個函式給 useEffect,這個函式會在元件「完成渲染並掛到畫面上」之後執行:

// useEffect 的最小寫法:每次渲染後都執行
import { useEffect } from "react";

function Greeting({ name }: { name: string }) {
  useEffect(() => {
    // 這段會在 render 之後執行
    document.title = `Hello, ${name}`;
  });

  return <h1>Hello, {name}!</h1>;
}

這段程式把「瀏覽器分頁標題」同步成目前的名字。每次 name 改變、元件重新渲染,useEffect 都會在「畫面真的更新到使用者眼前」之後再執行一次,把 document.title 改成新的值。注意它不是「在渲染時執行」,而是「在渲染完成、React 把結果交給瀏覽器之後」才執行——這個時機差異是 useEffect 跟生命週期方法最大的差別。

為什麼說「不是生命週期方法」

從 class 元件時代過來的工程師會直覺把 useEffect 對應到 componentDidMount、componentDidUpdate、componentWillUnmount。這對應關係「能用但不精確」。官方文件明確表示:useEffect 不是「當元件掛載時執行」、也不是「當元件更新時執行」,而是「當某些值改變時,把這個函式跟那些值同步」。

舉例來說,下面這段程式碼,effect 並不是在「掛載時」跑一次,也不是在「更新時」跑一次,而是在「count 改變時」跑一次:

// 當 count 改變時,把分頁標題同步成目前數字
function Counter({ count }: { count: number }) {
  useEffect(() => {
    document.title = `目前數字:${count}`;
  }, [count]); // 依賴陣列:只有 count 改變時才執行

  return <p>目前數字:{count}</p>;
}

心智模型應該是「effect 是把元件跟某個外部系統同步的橋樑」,而不是「元件在生命週期某個階段執行什麼」。這個差別看似抽象,但它會直接影響你怎麼寫 cleanup、怎麼選依賴、怎麼決定要不要把 useEffect 拆開。

依賴陣列:useEffect 的核心規則

useEffect 的第二個參數是「依賴陣列」,這是 useEffect 最容易踩雷的地方。React 用這個陣列決定「什麼時候該重新執行 effect」。三種寫法對應三種行為:

  1. 不傳第二個參數:每次 render 後都執行(effect 在 mount、update、context 變化時都會跑)。
  2. 傳空陣列 []:只有「第一次掛載」和「元件被移除」時會執行,後續 re-render 都不跑。
  3. 傳帶值的陣列 [a, b]:只有當陣列裡的值「跟上一次相比有變化」時才執行。

React 內建了一個 ESLint 規則 react-hooks/exhaustive-deps(Day 2 設定 ESLint 時已經啟用),它會在 effect 函式裡用到某個變數、卻沒把它放進依賴陣列時跳警告。這個警告看起來囉嗦,但它能擋下 90% 的 useEffect bug。寫 React 的紀律是:不要試圖關掉它。

以下是三種寫法的對照範例:

// 三種依賴陣列的行為對照
import { useEffect, useState } from "react";

function Demo() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("訪客");

  // 寫法 1:不傳依賴:每次 render 後都跑
  useEffect(() => {
    console.log("每次 render 後都跑(count、name 任一改變)");
  });

  // 寫法 2:空陣列:只在 mount 與元件被移除時跑
  useEffect(() => {
    console.log("只在元件掛載時跑一次");
    return () => console.log("元件被移除時跑 cleanup");
  }, []);

  // 寫法 3:明確列出依賴:count 改變時才跑
  useEffect(() => {
    console.log(`count 變成 ${count} 時跑`);
    document.title = `目前 ${count}`;
  }, [count]);

  return (
    <div>
      <p>{name}:{count}</p>
      <button onClick={() => setCount((c) => c + 1)}>+1</button>
      <button onClick={() => setName("工程師")}>改名</button>
    </div>
  );
}

在這段程式裡,「寫法 3」只會在 count 改變時印 log、改分頁標題;當 name 改變時,這個 effect 不會重新執行,因為 name 不在它的依賴裡。這就是 useEffect「精準同步外部系統」的能力——你告訴它「我只在乎 count」,它就不會在無關的 state 變動時打擾你。

物件與函式當依賴的陷阱

依賴陣列是用「淺比較(Object.is)」來判斷變化的。這帶來一個新手常踩的雷:如果你在 render 函式裡「現場建立」一個物件或函式當依賴,它每次 render 都是新的參考,effect 就會被觸發:

// 反例:每次 render 都建立新的 options 物件,effect 會無限觸發
function SearchBox() {
  const [keyword, setKeyword] = useState("");
  const options = { limit: 10, lang: "zh-TW" }; // 每次 render 都是新物件

  useEffect(() => {
    fetch(`/api/search?q=${keyword}`, options);
  }, [keyword, options]); // options 每次都「變」,等於每次都打 API

  return <input value={keyword} onChange={(e) => setKeyword(e.target.value)} />;
}

修法有三種:把物件宣告移到元件外(變成模組常數)、用 useMemo 包起來、或在 effect 內部只取需要的純量。useMemo 會在 Day 10 講到,今天先知道「現場建立物件當依賴會壞掉」就夠了。

cleanup:把訂閱、計時器、事件清乾淨

useEffect 的 callback 可以「回傳另一個函式」,那個函式被稱為 cleanup。React 會在「下一次 effect 執行前」以及「元件被移除時」呼叫 cleanup。它的用途是「把你先前打開的東西關掉」:取消訂閱、清除計時器、移除事件監聽、關閉 WebSocket、中止 fetch。忘記寫 cleanup 是 React 專案最常見的記憶體洩漏來源。

cleanup 的執行時機有兩個。第一個是「下次 effect 重新執行前」:當依賴改變、effect 要再跑一次時,React 會先跑上一次的 cleanup,再執行新的 effect。這能保證「外部系統不會同時被兩個 effect 訂閱」。第二個是「元件被移除時」:元件從畫面消失時,cleanup 會被執行一次,把資源釋放掉。

以下是 cleanup 的標準寫法範例:

// cleanup 標準寫法:訂閱事件 + 元件被移除時取消
import { useEffect, useState } from "react";

function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    // 註冊瀏覽器事件:當視窗寬度改變時更新 state
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);

    // cleanup:元件被移除時(或下次 effect 重跑前)移除監聽
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []); // 空陣列:這個 effect 只在 mount/移除時跑一次

  return <p>目前寬度:{width}px</p>;
}

如果忘記寫 removeEventListener,這個元件被反覆掛載與移除時,每次都會留下一個監聽,最後視窗 resize 一次就更新好幾次 state——這是經典的記憶體洩漏。記住一個口訣:「只要 effect 內部做了『加』,cleanup 就必須做對應的『減』:加事件就移除事件、加計時器就清除計時器、訂閱就取消訂閱、打 fetch 就 abort。」

計時器與 abort 的 cleanup

另一個常見的情境是計時器與 fetch 中止。AbortController 是瀏覽器原生 API,可以用來中斷進行中的 fetch;React 文件在資料取得章節(Day 14 會講)會大量使用:

// 計時器與 fetch 的 cleanup
import { useEffect, useState } from "react";

function PriceTicker({ symbol }: { symbol: string }) {
  const [price, setPrice] = useState<number | null>(null);

  useEffect(() => {
    let cancelled = false;
    const ctrl = new AbortController();

    async function fetchPrice() {
      const res = await fetch(`/api/price/${symbol}`, { signal: ctrl.signal });
      const data = await res.json();
      if (!cancelled) setPrice(data.price); // 用 cancelled 旗標避免寫到元件被移除後的 state
    }
    fetchPrice();

    const id = setInterval(fetchPrice, 5000); // 每 5 秒更新

    return () => {
      cancelled = true;       // 標記「這個 effect 的後續更新都不該生效」
      ctrl.abort();           // 中斷進行中的 fetch
      clearInterval(id);      // 清除計時器
    };
  }, [symbol]); // symbol 改變時,重新訂閱

  return <p>{symbol} 價格:{price ?? "載入中"}</p>;
}

這段範例把三件事一次示範完:cleanup 內設 cancelled 旗標避免在元件被移除後還呼叫 setState、用 AbortController 中斷還沒回來的 fetch、用 clearInterval 清掉計時器。React 19 之後即使你在元件被移除後呼叫 setState 也不再跳警告(19 之前會跳「Can't perform a React state update on an unmounted component」),但「不更新到已被移除的元件」仍然是正確的設計,cancelled 旗標繼續推薦使用。

什麼時候「不該」用 useEffect

這是 useEffect 章節最重要的觀念:很多時候新手把 useEffect 當萬靈丹,但其實根本不需要。以下是四個常見的「不該用」情境。

第一種:「只是要把某個 state 衍生出另一個值」。這不該用 useEffect,因為 state 改變時 render 函式本身就會重新跑,直接在 render 裡計算就好:

// 反例:用 useEffect 衍生狀態(多一次 render、還可能抓到舊值)
function Cart({ items }: { items: Item[] }) {
  const [total, setTotal] = useState(0);
  useEffect(() => {
    setTotal(items.reduce((sum, i) => sum + i.price, 0));
  }, [items]);
  return <p>總金額:{total}</p>;
}

// 正解:直接在 render 計算
function Cart({ items }: { items: Item[] }) {
  const total = items.reduce((sum, i) => sum + i.price, 0);
  return <p>總金額:{total}</p>;
}

反例的問題是「兩次 render」:第一次 render 時 total 是 0,使用者會看到短暫的 0;effect 跑完後 setTotal 觸發第二次 render,總金額才正確。正解在 render 函式裡就算出 total,沒有任何延遲。

第二種:「響應使用者事件而做的事」。例如「按下按鈕時送出表單」,這該寫在 onClick 裡,不該寫在 useEffect:

// 反例:用 useEffect 監聽某個 state 變化去打 API
function SubmitButton() {
  const [shouldSubmit, setShouldSubmit] = useState(false);
  useEffect(() => {
    if (shouldSubmit) fetch("/api/submit", { method: "POST" });
  }, [shouldSubmit]);
  return <button onClick={() => setShouldSubmit(true)}>送出</button>;
}

// 正解:直接在 onClick 裡打 API
function SubmitButton() {
  return <button onClick={() => fetch("/api/submit", { method: "POST" })}>送出</button>;
}

反例多繞了一圈「設 state → 等 render → effect 才跑」才打到 API,徒增複雜度也浪費一次 render。

第三種:「重置某個 state 當 prop 改變」。React 19 提供一個專門的 Key 重置機制(用 key 讓 React 重新掛載元件實例),但更常見的是把這個責任放在「真正擁有狀態的上層元件」。如果某個 prop 改變時你想清空某個內部 state,那個 state 很可能「不屬於這個元件」——把它提升到上層去管,或改用派生 state。

第四種:「可以直接在事件處理內做的事」。例如使用者點選某個選項時把 ID 寫進 localStorage,這該寫在 onClick,不該寫在 effect 裡「當 id 改變時同步到 localStorage」。差別在於「事件 vs 狀態」:事件觸發就該即時處理;只有「狀態要跟外部系統同步」才需要 effect。

何時「真的該用」useEffect

把上面的反面拿掉之後,useEffect 真正該出現的場景就清楚了:當你的元件需要跟某個「不歸 React 管」的外部系統同步時。例如:訂閱瀏覽器事件、跟 WebSocket 連線、把資料寫進第三方動畫庫、把狀態同步到 localStorage 之外的非 React 儲存體、與第三方命令式 API 整合(地圖、視訊播放器)。如果你的程式碼沒有觸碰「React 之外的系統」,多半不該用 useEffect。

完整實作:自動儲存到 localStorage 的筆記元件

把上面的觀念整合起來,做一個「自動儲存到 localStorage」的筆記元件。功能:使用者輸入文字時,每次內容改變就自動存進 localStorage;元件被移除時不做額外事(資料已經存過了)。這是一個經典的「同步到 React 外部系統」案例,剛好示範依賴陣列 + cleanup 的搭配。

先安裝依賴(在既有 Vite 專案裡裝 zod 做驗證,TypeScript 型別則來自 React 內建):

# 如果還沒裝過 zod,可以加這行;本篇主要靠 React 內建型別
pnpm add zod

接著寫元件本體。為了讓程式碼結構清楚,把它分成「讀取」、「寫入」、「元件」三段:

// src/hooks/usePersistentDraft.ts
// 自訂 Hook:把字串同步到 localStorage
// 這個 Hook 會在後續文章陸續擴充,這裡先給出最小版本

import { useEffect, useState } from "react";

const STORAGE_KEY = "note:draft";

export function usePersistentDraft() {
  // 第一次 render 時從 localStorage 讀初值(延遲初始化)
  const [text, setText] = useState<string>(() => {
    if (typeof window === "undefined") return "";
    return window.localStorage.getItem(STORAGE_KEY) ?? "";
  });

  // 每次 text 改變時同步寫回 localStorage
  useEffect(() => {
    window.localStorage.setItem(STORAGE_KEY, text);
  }, [text]);

  // 不需要 cleanup:localStorage.setItem 是一次性寫入,沒有「訂閱」或「計時器」需要釋放
  // 但若改成 IndexedDB 連線或 WebSocket,就必須寫 cleanup
  return [text, setText] as const;
}

上面的 useEffect 有兩個重點:第一,依賴陣列只有 [text],所以只有 text 改變時才會重跑;其他 state 變動(例如之後要加的字數統計)不會觸發寫入。第二,這個 effect 不需要 cleanup——localStorage.setItem 是一次性寫入,沒有「釋放資源」的動作。如果未來改成 IndexedDB 連線(會打開 transaction),那就要在 cleanup 裡把 transaction 結束。

接著寫元件本身,把 usePersistentDraft 用起來:

// src/components/NoteEditor.tsx
// 自動儲存筆記元件:輸入時即時存到 localStorage
import { usePersistentDraft } from "../hooks/usePersistentDraft";

export function NoteEditor() {
  const [text, setText] = usePersistentDraft();

  return (
    <div className="mx-auto max-w-xl space-y-2 p-4">
      <label htmlFor="note" className="block text-sm font-medium text-slate-700">
        草稿(自動儲存)
      </label>
      <textarea
        id="note"
        value={text}
        onChange={(e) => setText(e.target.value)}
        rows={6}
        className="w-full rounded border border-slate-300 p-2 text-sm"
        placeholder="開始打字,內容會自動存到 localStorage"
      />
      <p className="text-xs text-slate-500">
        目前 {text.length} 字(重整後仍會保留)
      </p>
    </div>
  );
}

這個元件展示了「useEffect 用來跟外部系統同步」的核心場景:localStorage 是 React 管不到的儲存體,我們透過 useEffect 在 text 改變時同步過去。沒有 effect 的話,每次 setText 觸發的 re-render 不會自動把資料寫進 localStorage,使用者重整頁面就會看到空白。

把元件掛到 App 並啟動開發伺服器:

// src/App.tsx
import { NoteEditor } from "./components/NoteEditor";

export default function App() {
  return (
    <main className="min-h-screen bg-slate-50">
      <NoteEditor />
    </main>
  );
}

// 終端機:pnpm dev
// 輸出:VITE v7.x.x  ready in xxx ms
// 輸出:Local:   http://localhost:5173/

打開瀏覽器試打幾個字,重新整理頁面,會發現字還在。這個行為已經驗證了 useEffect 的同步效果。如果你打開 React DevTools 並裝上 Profiler,可以看到 text 每次改變都會觸發一次 effect;不過這次 effect 內部只做一次 setItem,不會造成額外 re-render,效能上完全沒問題。

常見錯誤與踩雷

第一個最常見的踩雷是「忘記寫 cleanup」。例如在 effect 裡加了 window.addEventListener 卻沒在 cleanup 移除,元件被反覆掛載與移除(典型情境:路由切換)後,每次都會多一個監聽,最後一個 resize 事件可能更新好幾次 state。記住「有加就有減」這個口訣:加事件就要移除事件、加計時器就要清除計時器、訂閱就取消訂閱。

第二個踩雷是「依賴陣列漏寫」。例如 effect 裡用到 props.userId 卻沒把它放進依賴,於是元件在 userId 改變時不會重新打 API,仍在使用舊的 userId。這時 ESLint 的 react-hooks/exhaustive-deps 會跳警告,請不要 disable 它。如果你看到警告覺得「我知道我在幹嘛」,請先停下來確認一次——八成是 bug。

第三個踩雷是「在 effect 裡呼叫 setState 卻造成無限迴圈」。最常見的寫法是「依賴陣列裡放了某個每次 render 都是全新參考的物件」,例如 [{ a: 1 }] 或 [() => doSomething()]。這會讓 effect 每次 render 都「認為依賴變了」,於是無限觸發。修法是用 useMemo、useCallback,或把物件宣告搬到元件外。

第四個踩雷是「用 useEffect 處理 SSR(伺服器端渲染)會讀不到的 API」。例如直接呼叫 window.localStorage,在 Next.js 伺服器渲染時會出錯(沒有 window)。修法是把存取包在 typeof window !== "undefined" 判斷內,或把元件標記為 client component(Day 20 會講)。今天範例已經預先用 typeof 判斷過,實際搬到 Next.js 時不會壞。

第五個踩雷是「用 useEffect 模擬 componentDidMount」。常見情境是「我想在元件掛載時打一次 API」,於是寫 useEffect(fn, [])。這能跑,但不是最佳寫法——React 官方與社群都推薦用專門的資料取得函式庫(Day 14 會教 TanStack Query),它幫你處理快取、重試、競態、stale 狀態,比手寫 useEffect 健壯得多。

效能與實務提醒

useEffect 的執行時機是「render 完之後」,這代表 effect 不會阻塞畫面渲染。對於使用者來說,即使 effect 內部做了一點重的 IO(例如 fetch),畫面還是會先顯示出來。這是 React 的設計選擇:把「同步的 DOM 更新」放在最優先,把「可能要花時間的副作用」放到 render 之後。

不過這個特性也帶來副作用:第一次 render 時 effect 還沒跑完,所以畫面上的 state 可能是「舊的」或「空值」。這對資料取得元件特別重要——你必須設計好「載入中」、「錯誤」、「完成」三種狀態,避免使用者看到一片空白或閃爍的內容。Day 14 會專門展開這個主題。

另一個實務提醒是「把相關的 effect 拆開」。與其在元件裡寫一個巨大的 useEffect 把所有副作用都塞進去,不如按照「要同步的外部系統」拆成多個小 effect。例如一個元件要同時「訂閱瀏覽器事件」與「打 API 拿資料」,就應該寫成兩個 useEffect,不要混在一起。這樣每個 effect 的依賴都很明確、除錯時也容易定位。

最後一個提醒是 StrictMode 的雙重呼叫。在開發模式下,React 的 StrictMode 會刻意把每個元件 mount 兩次,目的是抓出「不純」的 effect——例如忘了寫 cleanup、或 cleanup 寫錯。如果你的 cleanup 寫得不嚴謹(例如只呼叫了 removeEventListener 但沒清計時器),在 StrictMode 下會被立刻抓出來。當你看到「某個 effect 跑兩次、cleanup 也跑兩次」的 log,別慌,這是開發模式的正常行為,正式上線後只會跑一次。

小結

今天我們把 useEffect 的本質、依賴陣列規則、cleanup 寫法、以及「什麼時候不該用」一次攤開來了。重點回顧:useEffect 是把 React 狀態跟「外部系統」同步的橋樑,不是生命週期方法;依賴陣列用淺比較決定是否重新執行,記得用 ESLint exhaustive-deps 規則把漏寫的依賴擋下;cleanup 在下次 effect 執行前與元件被移除時執行,「有加就有減」是必須遵守的紀律;很多新手以為需要 useEffect 的場景其實可以直接在 render 或事件裡完成,不需要 effect。我們也實作了一個會自動儲存到 localStorage 的筆記元件,示範了真實的「同步到 React 外部系統」場景。明天 Day 10 會在這個基礎上介紹「自訂 Hook」,把 useEffect 的邏輯抽成可重用的函式,解決「兩個元件都需要自動儲存草稿」這類需求。

結語

明天,我們會把 useEffect 的邏輯抽成「自訂 Hook」——一種 React 內建的「邏輯重用」機制。我們會把今天的 usePersistentDraft 擴充成支援 schema 驗證的版本,再用它做一個「同步多個欄位」的範例,體會「Hook 讓元件只剩畫面、邏輯全部集中到 Hook」的清爽分工。記得今天的 NoteEditor 與 usePersistentDraft 先留著,明天會在同一個專案上把它升級成更通用的版本。

延伸資源

  • React 官方〈Synchronizing with Effects〉(19.x,2026 年 3 月):https://react.dev/learn/synchronizing-with-effects
  • React 官方〈You Might Not Need an Effect〉(19.x):https://react.dev/learn/you-might-not-need-an-effect
  • React 官方〈useEffect 參考〉(19.x):https://react.dev/reference/react/useEffect
  • MDN〈AbortController〉:https://developer.mozilla.org/zh-TW/docs/Web/API/AbortController
  • React Hooks ESLint 插件(exhaustive-deps 規則):https://www.npmjs.com/package/eslint-plugin-react-hooks

留言

這個網誌中的熱門文章

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