跳到主要內容

DE Day 21 每日自動更新:排程腳本

DE Day 21 每日自動更新:排程腳本

執行需求:CPU 可跑。今天把 Day 20 的增量抓取邏輯掛上排程,讓它每天自動跑。我們會用 APScheduler 3.11(2025 年 11 月的穩定版本)實作一支可在背景運行的排程腳本,並設計「時區、夏令時間、資源競爭、單機限制」四個常見陷阱的對應做法。版本基準仍是 2025 年 11 月:Python 3.13、APScheduler 3.11、DuckDB 1.4、httpx 0.28。本篇不依賴任何雲端服務,全部在本機的 Linux、macOS 或 Windows 都能跑。

引言

Day 20 我們建立了「增量抓取」的工程設計:水位標記(watermark)與冪等(idempotent)。但這支腳本目前還是要手動執行。資料工程的真正價值,是讓這支腳本每天「自動執行」——在你睡覺的時候、把昨天的資料抓回來、寫進 DuckDB、並把結果寫成儀表板。今天我們就把這個能力做出來。

「排程(scheduling)」在資料工程裡是個獨立的小領域,有四個常見陷阱。第一是時區:你寫「每天凌晨三點跑」,在台灣是早上 11 點、在美國西岸是早上 7 點、在歐洲是下午 4 點;如果你在跨國團隊,這個時間會出問題。第二是夏令時間:台灣沒有夏令時間,但美國與歐洲有;如果你的程式碼寫死 UTC 偏移,碰到夏令切換日會差一個小時。第三是資源競爭:兩支腳本同時打開同一個 DuckDB 檔案會出錯,需要 lock 機制。第四是單機限制:你的機器終究會關機,硬碟會滿,網路會斷;本機排程只是「小規模的自動化」,真正能撐住每天運轉的是「雲端排程」(Day 37 的 GitHub Actions)或「編排系統」(Day 27 的 Airflow)。

今天的目標有四個:第一,用 APScheduler 3.11 寫一支「每天固定時間跑」的本機排程腳本;第二,把 Day 20 的抓取邏輯包成可被排程呼叫的工作函式;第三,設計「啟動、執行、失敗、結束」四個狀態的紀錄;第四,處理時區與資源競爭這兩個常見陷阱。讀完之後你會知道怎麼讓一支 Python 腳本「在你睡覺時自己跑、跑完留下紀錄、出問題時通知你」,這是資料工程從「個人工具」走到「團隊資產」的關鍵一步。

APScheduler 的設計與版本選擇

APScheduler(Advanced Python Scheduler)是 Python 生態最常用的排程函式庫,從 2008 年發展至今,已有 3.x 穩定版。它的設計哲學是「讓 Python 程式內建排程能力」,不需要另外裝 cron 或 Windows Task Scheduler。對資料工程來說,這個特性很實用:我們可以把抓取邏輯與排程邏輯寫在同一支程式裡,部署時只要把程式跑起來,排程就生效。APScheduler 3.11 是 2025 年 11 月的穩定版本,支援 Python 3.8 到 3.13,介面與 3.x 系列一致,沒有 breaking change。4.x 仍在 alpha,這裡不採用。

APScheduler 有四種排程器(scheduler)。BlockingScheduler 是最簡單的,在前景執行,適合一支獨立腳本;BackgroundScheduler 在背景執行,適合與其他程式共存;AsyncIOScheduler 與 asyncio 整合,適合非同步抓取;TornadoScheduler 與 Tornado 整合,今天不會用到。對一支每天跑一次的抓取腳本,BlockingScheduler 或 BackgroundScheduler 就夠;如果你要一支程式同時跑抓取與網頁伺服器(譬如 Day 34 的 Streamlit),用 BackgroundScheduler。

另一個關鍵概念是「觸發器(trigger)」,決定任務何時執行。date 在指定時間跑一次;interval 每隔固定時間跑;cron 用 cron 語法定義週期性任務。我們的場景適合 cron,因為每天固定時間跑是 cron 的標準用法。APScheduler 的 cron 與 Linux cron 略有不同,它多了「秒」欄位,且時區可以用 timezone 參數指定,比 Linux cron 更彈性。

完整實作:APScheduler 排程腳本

今天的實作分四階段。第一階段用 APScheduler 寫一支最簡的「每秒印一行」的排程腳本,確認環境正常;第二階段把 Day 20 的抓取邏輯包成可被排程呼叫的工作函式;第三階段加上啟動、執行、失敗的紀錄;第四階段處理時區、單實例與資源競爭。我們沿用 Day 19 的 _mock_api.py 做為抓取目標,用 DuckDB 1.4 做為落地。

第一步:安裝 APScheduler 並沿用前面的套件:

cd de-journey
uv pip install apscheduler==3.11.0 httpx==0.28.1 duckdb==1.4.1
# 沿用 Day 17、Day 19、Day 20 的 _etiquette.py、_pager.py、_watermark.py

第二步:寫一支最簡的排程腳本,確認 APScheduler 環境正常:

"""pipelines/day21_hello.py:APScheduler 最簡範例,每秒印一行。"""
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler

def tick() -> None:
    print(f"[{datetime.now():%H:%M:%S}] tick")

scheduler = BlockingScheduler()
scheduler.add_job(tick, "interval", seconds=2, id="hello_tick")
print("排程啟動,按 Ctrl+C 結束")
try:
    scheduler.start()
except (KeyboardInterrupt, SystemExit):
    print("排程結束")

這段是 APScheduler 的「Hello World」。BlockingScheduler 在前景執行,add_job(tick, "interval", seconds=2) 表示每 2 秒呼叫 tick() 一次。id 是任務識別碼,方便後續移除或查詢。執行後會看到每 2 秒印出一行時間戳;按 Ctrl+C 結束。這個範例確認 APScheduler 安裝正確且時鐘正常。

第三步:把 Day 20 的抓取邏輯包成可被排程呼叫的工作函式:

"""pipelines/_scheduled_jobs.py:包裝 Day 20 的抓取邏輯為可排程的 job。"""
import asyncio
from datetime import datetime, timezone
from pathlib import Path
import duckdb
import httpx
from pipelines._etiquette import PoliteSession
from pipelines._pager import paginate_offset
from pipelines._watermark import get_watermark, set_watermark

UA = "HaoBot/1.0 (+https://blog.hao-code.com/bots)"
SOURCE = "demo_api"
URL = "http://127.0.0.1:8766/api/offset"
RUN_LOG = Path("logs/day21_run.jsonl")
RUN_LOG.parent.mkdir(exist_ok=True)

def write_run_log(status: str, n_rows: int, message: str) -> None:
    import json
    rec = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "status": status,
        "n_rows": n_rows,
        "message": message,
    }
    with RUN_LOG.open("a", encoding="utf-8") as f:
        f.write(json.dumps(rec, ensure_ascii=False) + "\n")

def fetch_job() -> None:
    """單次抓取工作;給 APScheduler 呼叫。"""
    write_run_log("start", 0, "fetch_job 開始")
    try:
        n = asyncio.run(_fetch_async())
        write_run_log("success", n, f"本次新增 {n} 筆")
    except Exception as e:
        write_run_log("fail", 0, f"{type(e).__name__}: {e}")
        raise

async def _fetch_async() -> int:
    con = duckdb.connect("warehouse/de-journey.duckdb")
    con.execute("""
        CREATE TABLE IF NOT EXISTS raw.day21_items (
            id INTEGER PRIMARY KEY,
            name VARCHAR, ts BIGINT, fetched_at TIMESTAMP
        )
    """)
    wm = get_watermark(con, SOURCE)
    wm_ts = int(wm[1]) if wm else 0

    total = 0
    async with httpx.AsyncClient() as client:
        session = PoliteSession(UA, qps=3.0)
        max_ts = wm_ts
        async for page in paginate_offset(client, URL, limit=50, session=session):
            if not page.items:
                break
            fresh = [it for it in page.items if it["ts"] > wm_ts]
            if not fresh:
                continue
            now = datetime.now(timezone.utc)
            con.executemany(
                "INSERT INTO raw.day21_items VALUES (?, ?, ?, ?) "
                "ON CONFLICT (id) DO UPDATE SET "
                "name = EXCLUDED.name, ts = EXCLUDED.ts, fetched_at = EXCLUDED.fetched_at",
                [(it["id"], it["name"], it["ts"], now) for it in fresh],
            )
            total += len(fresh)
            max_ts = max(max_ts, max(it["ts"] for it in fresh))
        set_watermark(con, SOURCE, "ts", str(max_ts), total)
    return total

if __name__ == "__main__":
    fetch_job()

這段把 Day 20 的增量邏輯包成 fetch_job() 函式,可被 APScheduler 呼叫。重點有三:第一,fetch_job() 是同步函式,內部用 asyncio.run() 包 async 邏輯,這是 APScheduler 呼叫 async 函式的標準做法;第二,write_run_log() 寫 JSON Lines 到 logs/day21_run.jsonl,這是排程腳本的「健康檢查」,出事時第一個查這個檔;第三,try/except 把例外拋回去,讓 APScheduler 知道任務失敗,這樣它才能記錄錯誤並通知(Day 22 會展開)。

第四步:寫正式的排程腳本,用 cron 觸發器每天凌晨三點跑:

"""pipelines/day21_scheduler.py:每天凌晨 3 點跑 fetch_job。"""
from datetime import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
from pipelines._scheduled_jobs import fetch_job

scheduler = BlockingScheduler(timezone="Asia/Taipei")
scheduler.add_job(
    fetch_job,
    CronTrigger(hour=3, minute=0, timezone="Asia/Taipei"),
    id="daily_fetch",
    name="每日增量抓取",
    misfire_grace_time=600,
    coalesce=True,
    max_instances=1,
)
print(f"[{datetime.now():%Y-%m-%d %H:%M:%S}] 排程啟動,下次執行:{scheduler.get_job('daily_fetch').next_run_time}")
try:
    scheduler.start()
except (KeyboardInterrupt, SystemExit):
    print("排程結束")

這段是今天的工程核心。CronTrigger(hour=3, minute=0, timezone="Asia/Taipei") 設定每天台北時間 03:00 跑。misfire_grace_time=600 表示如果排程時間到了但程式還在跑(或剛開機),最多寬限 600 秒(10 分鐘)執行;超過就跳過。coalesce=True 表示多次錯過合併成一次執行,避免「程式關機 3 天、開機時連跑 3 次」。max_instances=1 是「單實例」保護,避免兩個排程同時跑。這三個參數合起來是排程腳本的「保險絲」。

第五步:用一個守護程式(daemon)模式讓腳本在背景執行,並提供「單實例」鎖,避免兩個行程同時跑:

"""pipelines/day21_daemon.py:用檔案鎖實作單實例保護。"""
import fcntl
import sys
import time
from pathlib import Path

LOCK_PATH = Path("logs/day21.lock")
LOCK_PATH.parent.mkdir(exist_ok=True)

def acquire_lock() -> int | None:
    """回傳 lock fd,若已被佔用則回傳 None。"""
    fd = open(LOCK_PATH, "w")
    try:
        fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
        fd.write(str(time.time()))
        fd.flush()
        return fd
    except BlockingIOError:
        fd.close()
        return None

if __name__ == "__main__":
    fd = acquire_lock()
    if fd is None:
        print(f"已有另一個實例在跑(lock: {LOCK_PATH})")
        sys.exit(1)
    print("取得 lock,啟動排程")
    from pipelines.day21_scheduler import scheduler  # type: ignore
    try:
        scheduler.start()
    finally:
        fd.close()
        LOCK_PATH.unlink(missing_ok=True)

這段用 fcntl.flock 做檔案鎖,這是 Linux 與 macOS 的標準做法(Windows 不支援 flock,要在 Windows 換成 msvcrt.locking)。這個鎖的意義是:如果有兩個排程腳本同時被啟動,第二個會立刻失敗並退出,避免「兩個抓取同時打開同一個 DuckDB 檔案」造成 lock 衝突。執行 python pipelines/day21_daemon.py 啟動後,再開另一個終端機跑同樣指令,會看到「已有另一個實例在跑」並退出。這是 Day 22 失敗處理會延伸的「分散式鎖」概念的單機版。

第六步:用 APScheduler 的事件監聽器把「任務開始、結束、失敗」全部寫進 log:

"""pipelines/day21_with_events.py:用事件監聽器記錄任務生命週期。"""
from datetime import datetime
from pathlib import Path
import json
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR
from pipelines._scheduled_jobs import fetch_job

LOG = Path("logs/day21_events.jsonl")
LOG.parent.mkdir(exist_ok=True)

def write_event(event_type: str, job_id: str, **details) -> None:
    rec = {"ts": datetime.now().isoformat(), "type": event_type, "job": job_id, **details}
    with LOG.open("a", encoding="utf-8") as f:
        f.write(json.dumps(rec, ensure_ascii=False) + "\n")

def on_success(event) -> None:
    write_event("success", event.job_id, runtime_ms=event.scheduled_run_time.timestamp())

def on_error(event) -> None:
    write_event("error", event.job_id, exc=repr(event.exception))

scheduler = BlockingScheduler(timezone="Asia/Taipei")
scheduler.add_job(fetch_job, "cron", hour=3, minute=0, id="daily_fetch",
                  misfire_grace_time=600, coalesce=True, max_instances=1)
scheduler.add_listener(on_success, EVENT_JOB_EXECUTED)
scheduler.add_listener(on_error, EVENT_JOB_ERROR)
print(f"啟動時間:{datetime.now():%Y-%m-%d %H:%M:%S}")
scheduler.start()

這段在排程器層級加上事件監聽,把「任務執行成功」與「任務執行失敗」都寫進 logs/day21_events.jsonl。EVENT_JOB_EXECUTED 與 EVENT_JOB_ERROR 是 APScheduler 的標準事件,搭配 add_listener() 註冊 handler。實際執行後,每天 03:00 會看到 {"type": "success", "job": "daily_fetch", ...} 寫進 log;抓取失敗時則是 {"type": "error", "exc": "..."}。Day 22 的失敗處理會把這個 log 接到通知機制。

第七步:把排程設計的關鍵參數寫成可被環境變數覆寫的設定:

"""pipelines/_schedule_config.py:把 cron 與時區參數從環境變數讀。"""
import os
from dataclasses import dataclass

@dataclass(frozen=True)
class ScheduleConfig:
    hour: int
    minute: int
    timezone: str
    qps: float
    misfire_grace_sec: int

    @classmethod
    def from_env(cls) -> "ScheduleConfig":
        return cls(
            hour=int(os.environ.get("FETCH_HOUR", "3")),
            minute=int(os.environ.get("FETCH_MINUTE", "0")),
            timezone=os.environ.get("FETCH_TZ", "Asia/Taipei"),
            qps=float(os.environ.get("FETCH_QPS", "3.0")),
            misfire_grace_sec=int(os.environ.get("FETCH_GRACE", "600")),
        )

if __name__ == "__main__":
    print(ScheduleConfig.from_env())

這段把排程的關鍵參數(hour、minute、timezone、qps、grace)寫成 dataclass,從環境變數讀預設值。實務上,測試環境可能希望每小時跑一次,正式環境希望每天凌晨 3 點跑;用環境變數切換比改程式碼乾淨。dataclass(frozen=True) 保證設定不可變,避免「跑到一半被改掉」的競態。Day 30 的端到端管線會用更完整的 pydantic-settings 來管這類設定。

另一個常見需求是「多個工作彼此相依」。APScheduler 可以用 add_job(... , depends_on=...) 表達「B 工作必須在 A 工作完成後才跑」。實務上,如果相依關係很複雜(譬如五個工作彼此牽連),建議改用 Airflow 或 Prefect 這類專門的編排系統。APScheduler 適合「獨立任務」或「線性相依」;複雜 DAG 留給 Day 27-28。

把這支排程跑起來的方法也值得展開一下。最常見的做法有兩種。第一是「直接跑」:在終端機下 python pipelines/day21_daemon.py 並用 nohup 讓它在背景持續執行,重啟需要手動。第二是「用 systemd 管」(Linux):寫一個 .service 檔交給 systemd,systemd 會負責開機啟動、崩潰重啟、log 收集。這套做法在 Day 36 的部署章節會再展開。今天先用 nohup 跑起來驗證功能,正式部署再升級成 systemd 或容器化。

排程腳本本身需要驗證才能上線。一個簡單的「煙霧測試」是用 interval 觸發器跑短間隔(譬如每分鐘),觀察 24 小時後 log 是否都正常寫入;確認沒問題再改成正式 cron。這是測試驅動的精神:先證明「能跑」再上正式排程。實務上很多人會把這套測試寫成 pytest 腳本,每天在 CI 跑一次。今天先記住這個觀念,明天 Day 22 會把失敗處理與通知機制納入測試。

另一個延伸是「排程日曆」。當一支管線有多個時間點(譬如每天 03:00 抓取、04:00 清洗、05:00 報表),最好有一張表把這些時間畫出來,方便日後維護與除錯。這張表可以寫進 README,也可以接到 Day 34 的 Streamlit 儀表板。今天的單一抓取任務簡單到不用畫,明天引入失敗處理後就會有用。

常見錯誤與踩雷

第一個雷是「忘記處理時區」。APScheduler 預設用本地時間,但本地時間會被系統時區影響;在 Docker 或雲端環境,系統時區常常是 UTC。對應排查方向:永遠在 BlockingScheduler(timezone="...") 與 CronTrigger(timezone="...") 明確指定時區;不要依賴系統預設。Day 37 的 GitHub Actions 預設 UTC,排程要明確寫 timezone="Asia/Taipei" 才會在台北凌晨三點跑。

第二個雷是「夏令時間讓任務跑兩次或漏跑」。如果你用 CronTrigger(timezone="US/Eastern") 排每天 02:30 跑,碰到春令切換(跳過 02:00–03:00)那天,這個時間根本不存在;秋令切換那天,這個時間會跑兩次。對應做法:避免在 02:00–03:00 這種時段排任務;或改用 UTC 排程,再把輸出轉當地時區。台灣沒有夏令時間,但若部署到跨國環境就要小心。

第三個雷是「同一個任務跑兩次」。APScheduler 的 max_instances=1 保護同一個排程器內的多觸發,但無法保護「兩個獨立的排程器行程」。對應做法:用上一段的檔案鎖 fcntl.flock,或用分散式鎖(譬如 Redis 的 SETNX)。如果不在意,重複跑通常會被水位與冪等擋下(Day 20 的設計),但仍會浪費資源。

第四個雷是「misfire 沒處理」。如果你的機器在排程時間關機、凌晨 3 點沒執行;早上 9 點開機時,APScheduler 會嘗試「補跑」這個任務,可能會跑出非預期的結果(例如你設計成「每天只跑一次」,但補跑變成兩次)。對應做法:設 misfire_grace_time 寬限秒數(譬如 600 秒),coalesce=True 把多次補跑合併成一次;或明確告訴 APScheduler「超過寬限就跳過」。

第五個雷是「沒留下 log 就上線」。一支排程腳本跑失敗了,但 log 沒寫,過幾天才發現「已經三天沒抓到資料」。對應做法:每次啟動、執行、結束、錯誤都寫 log;log 包含時間戳、任務 ID、執行時間、錯誤訊息;log 保留至少 30 天。這是 Day 22 失敗處理會延伸的「通知機制」前置。今天的 write_run_log() 與 write_event() 就是這個精神。

效能與實務提醒

APScheduler 是「單機排程」,不是「分散式排程」。它的設計假設一支程式跑在一台機器上;如果你的機器關機,排程就停。對應做法:正式環境通常用 Airflow(Day 27-28)或 GitHub Actions(Day 37)。今天學的是「怎麼在本機讓腳本自動跑」,這在開發與測試階段很實用,正式上線前再升級成編排系統。

另一個實務重點是「資源限制」。一支 Python 程式在背景執行久了,記憶體會累積、檔案 handle 會沒釋放。對應做法:定期重啟(譬如每週日凌晨),把重啟動作也寫進排程;或用 supervisor(Linux)當 watchdog,程式崩潰自動重啟。Day 36 的部署章節會展開這個議題。

「時間格式的陷阱」也是資料工程的常見 bug。datetime.now() 在 Python 沒有時區參數時回傳「naive datetime」,這個值在資料庫、API、log 之間傳遞時容易出錯。對應做法:永遠用 datetime.now(timezone.utc) 或 datetime.now(ZoneInfo("Asia/Taipei")) 拿到「aware datetime」,儲存時轉 UTC,顯示時轉當地時區。DuckDB 的 TIMESTAMPTZ 可以正確處理時區,不要用 TIMESTAMP(無時區)。

最後,排程的本質是「把人工重複的動作自動化」。一支好的排程腳本不只是「讓程式自己跑」,更要做到「跑得起來、跑得久、跑出問題找得到人」。把啟動紀錄、執行紀錄、失敗紀錄、log 路徑、聯絡人寫進 README,這是「管線活得比作者久」的關鍵。Day 22 的失敗處理會把這套延伸到「自動通知」與「自動重跑」,在那之前先把今天的排程架構打穩。

小結

今天把 Day 20 的增量抓取邏輯掛上 APScheduler 3.11,建立了完整的排程腳本。我們學了 APScheduler 的四種排程器、cron 觸發器、misfire_grace_time 與 coalesce 的觀念,並用檔案鎖實作單實例保護、用事件監聽器記錄任務生命週期。這套設計讓一支 Python 腳本可以「在你睡覺時自己跑、跑出問題時留紀錄」。把今天的關鍵詞整理進筆記本:APScheduler 3.11、BlockingScheduler、CronTrigger、misfire_grace_time、coalesce、max_instances、單實例鎖、naive/aware datetime、UTC。

把 pipelines/day21_scheduler.py、pipelines/_scheduled_jobs.py、pipelines/day21_daemon.py 都存起來。但光有排程不夠——抓取失敗了怎麼知道?怎麼自動重跑?明天 Day 22 我們會把這套排程加上「失敗通知、重試佇列、斷點續跑」三個關鍵設計。

結語

今天我們把「手動跑」變成「自動跑」,這是資料工程從「個人工具」走到「團隊資產」的關鍵一步。但一支「每天自動跑」的管線最怕的不是「沒跑」,而是「跑了但失敗」。明天 Day 22 我們會把失敗處理、警報、與斷點續跑一次講清楚:用 Sentinel 模式設計「失敗 → 通知 → 重試 → 放棄」的四級處理、用 check-point 機制實作斷點續跑、用 Slack 或 email 整合做即時通知。這套觀念會讓你的管線從「自動跑」升級為「自動跑、自動救」。

明天,我們會在今天的排程腳本上,加上失敗處理、警報通知與斷點續跑,讓管線具備「自我修復」的能力。

延伸資源

  • APScheduler 3.11 官方文件(2025):https://apscheduler.readthedocs.io/en/3.x/,BlockingScheduler、CronTrigger、misfire_grace_time 的標準用法。
  • Python datetime 時區處理官方文件(2025):https://docs.python.org/3/library/datetime.html,timezone 與 ZoneInfo 的標準用法。
  • Linux fcntl flock(2) 手冊(2025):https://man7.org/linux/man-pages/man2/flock.2.html,檔案鎖的標準用法與注意事項。
  • GitHub Actions cron 排程(2025):https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule,Day 37 會展開的雲端排程替代方案。
  • DuckDB 並行寫入說明(2025):https://duckdb.org/docs/connect/concurrency.html,單機 DuckDB 的並行限制與 lock 機制。

留言

這個網誌中的熱門文章

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