<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Python on YuChen</title><link>https://Dandelionlibra.github.io/tags/python/</link><description>Recent content in Python on YuChen</description><generator>Hugo -- gohugo.io</generator><language>zh-Hant-TW</language><lastBuildDate>Sat, 02 May 2026 21:40:44 +0800</lastBuildDate><atom:link href="https://Dandelionlibra.github.io/tags/python/index.xml" rel="self" type="application/rss+xml"/><item><title>如何使用 PyAutoGUI（一）：安裝與滑鼠控制</title><link>https://Dandelionlibra.github.io/2026/04/how-to-use-pyautogui-mouse/</link><pubDate>Thu, 30 Apr 2026 00:00:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/how-to-use-pyautogui-mouse/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-pyautogui-%e5%9f%ba%e6%9c%ac%e4%bb%8b%e7%b4%b9" &gt;PyAutoGUI 基本介紹&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%ae%89%e8%a3%9d-pyautogui" &gt;安裝 PyAutoGUI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e8%9e%a2%e5%b9%95%e5%ba%a7%e6%a8%99%e7%b3%bb%e7%b5%b1" &gt;螢幕座標系統&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%ae%89%e5%85%a8%e6%a9%9f%e5%88%b6fail-safe" &gt;安全機制：Fail-Safe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e5%8f%96%e5%be%97%e8%9e%a2%e5%b9%95%e8%b3%87%e8%a8%8a" &gt;取得螢幕資訊&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e6%bb%91%e9%bc%a0%e9%bb%9e%e6%93%8a" &gt;滑鼠點擊&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e9%bb%9e%e6%93%8a-click" &gt;點擊&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e9%9b%99%e6%93%8a-doubleclick" &gt;雙擊&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%8f%b3%e9%8d%b5%e9%bb%9e%e6%93%8a-rightclick" &gt;右鍵點擊&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e4%b8%ad%e9%8d%b5%e9%bb%9e%e6%93%8a-middleclick" &gt;中鍵點擊&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%88%86%e9%96%8b%e6%8c%89%e4%b8%8b%e8%88%87%e6%94%be%e9%96%8b-mousedown--mouseup" &gt;分開按下與放開&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e6%bb%91%e9%bc%a0%e7%a7%bb%e5%8b%95" &gt;滑鼠移動&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e7%a7%bb%e5%8b%95%e5%88%b0%e7%b5%95%e5%b0%8d%e5%ba%a7%e6%a8%99-movetox-y-durationnum_seconds" &gt;移動到絕對座標&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e7%a7%bb%e5%8b%95%e5%88%b0%e7%9b%b8%e5%b0%8d%e5%ba%a7%e6%a8%99-movelrelxoffset-yoffset-durationnum_seconds" &gt;移動到相對座標&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e6%8b%96%e6%9b%b3%e5%88%b0%e7%b5%95%e5%b0%8d%e5%ba%a7%e6%a8%99-dragtox-y-durationnum_seconds" &gt;拖曳到絕對座標&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e6%8b%96%e6%9b%b3%e5%88%b0%e7%9b%b8%e5%b0%8d%e5%ba%a7%e6%a8%99-dragrelxoffset-yoffset-durationnum_seconds" &gt;拖曳到相對座標&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#8-%e6%bb%be%e8%bc%aa%e6%93%8d%e4%bd%9c" &gt;滾輪操作&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%9e%82%e7%9b%b4%e6%bb%be%e5%8b%95-scroll" &gt;垂直滾動&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e6%b0%b4%e5%b9%b3%e6%bb%be%e5%8b%95-hscroll" &gt;水平滾動&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-pyautogui-基本介紹"&gt;1. PyAutoGUI 基本介紹
&lt;/h3&gt;&lt;p&gt;PyAutoGUI 是一個跨平台的 Python GUI 自動化套件，可以程式化地控制&lt;strong&gt;滑鼠&lt;/strong&gt;與&lt;strong&gt;鍵盤&lt;/strong&gt;，適用於 Windows、macOS 和 Linux。常見應用場景包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;重複性操作自動化&lt;/strong&gt;：自動填表、批次處理檔案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GUI 測試&lt;/strong&gt;：模擬使用者操作，進行介面測試。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;螢幕截圖與圖像辨識&lt;/strong&gt;：截圖並尋找特定圖案位置，再進行點擊。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 注意事項&lt;/strong&gt;&lt;br&gt;
PyAutoGUI 會直接控制系統的滑鼠與鍵盤，執行時請先確認不會誤操作重要視窗，建議先在測試環境中嘗試。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="2-安裝-pyautogui"&gt;2. 安裝 PyAutoGUI
&lt;/h3&gt;&lt;p&gt;在終端機輸入以下指令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;若有多個版本的 python 可以指定特定版本：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;py -3.8 -m pip install pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;macOS：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python3 -m pip install pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux：&lt;/strong&gt;&lt;br&gt;
還需要安裝額外依賴套件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo apt-get install python3-tk python3-dev scrot
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python3 -m pip install pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安裝完成後，驗證是否成功：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size()) &lt;span style="color:#75715e"&gt;# 印出螢幕解析度，例如 Size(width=1920, height=1080)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="3-螢幕座標系統"&gt;3. 螢幕座標系統
&lt;/h3&gt;&lt;p&gt;在開始自動化之前，我們必須先了解電腦螢幕的座標系統（Coordinate System）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;原點 (0, 0)&lt;/strong&gt;：位於螢幕的 &lt;strong&gt;左上角 (Top-Left)&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;X 軸&lt;/strong&gt;：水平方向，由左向右遞增。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Y 軸&lt;/strong&gt;：垂直方向，由上向下遞增。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你想更直觀地了解座標位置，可以使用下方我開發的小工具。移動滑鼠即可即時顯示座標，點擊畫面則可以釘選座標點。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;互動小工具：螢幕座標地圖&lt;/strong&gt;
建議點擊右上角的「全螢幕」按鈕或將瀏覽器視窗最大化，以獲得最準確的座標參考。&lt;br&gt;
程式碼已開源於：&lt;a class="link" href="https://dandelionlibra.github.io/tools/screen_coordinate_map.html" target="_blank" rel="noopener"
&gt;screen-coordinate-map&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;iframe src="https://dandelionlibra.github.io/tools/screen_coordinate_map.html" width="100%" height="500px" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; background: #0d0d0f;"&gt;&lt;/iframe&gt;
&lt;hr&gt;
&lt;h3 id="4-安全機制fail-safe"&gt;4. 安全機制：Fail-Safe
&lt;/h3&gt;&lt;p&gt;PyAutoGUI 內建一個防呆安全機制：&lt;strong&gt;當滑鼠快速移動到螢幕左上角（座標 0, 0）時，會自動丟出 &lt;code&gt;FailSafeException&lt;/code&gt; 並中止程式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;這個機制可以讓你在程式失控時，快速移動滑鼠到左上角以強制停止。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 安全機制，正式使用時請保持啟用（預設為 True）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;FAILSAFE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 若確定不需要此機制（不建議）可關閉&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;FAILSAFE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;此外，還有一個全域延遲設定，讓每個操作之間自動暫停，避免操作太快：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 每次 PyAutoGUI 函式呼叫後，暫停 0.5 秒（預設為 0.1）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;PAUSE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="5-取得螢幕資訊"&gt;5. 取得螢幕資訊
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 取得螢幕解析度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;width, height &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;螢幕寬度：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;width&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;，高度：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;height&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 取得滑鼠目前位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x, y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;position()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;目前滑鼠座標：(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;x&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;y&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 小技巧&lt;/strong&gt;：執行以下程式碼，可以即時追蹤滑鼠座標，按 &lt;code&gt;Ctrl+C&lt;/code&gt; 停止：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;,&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x, y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;position()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\r&lt;/span&gt;&lt;span style="color:#e6db74"&gt;目前座標：(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;x&lt;span style="color:#e6db74"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;4&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;y&lt;span style="color:#e6db74"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;4&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;, end&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, flush&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;KeyboardInterrupt&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# 按下 Ctrl+C 會中斷程式&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;已停止追蹤&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="6-滑鼠點擊"&gt;6. 滑鼠點擊
&lt;/h3&gt;&lt;h4 id="點擊-click"&gt;點擊 &lt;code&gt;click()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click() &lt;span style="color:#75715e"&gt;# 在目前位置點擊左鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 點擊指定座標&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;right&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 指定按鍵 &amp;#39;left&amp;#39;、&amp;#39;right&amp;#39;、&amp;#39;middle&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, clicks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 連續點擊&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, clicks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.25&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 設定兩次點擊之間的間隔（秒）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;right&amp;#39;&lt;/span&gt;, clicks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.25&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 指定按鍵並連續點擊&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="雙擊-doubleclick"&gt;雙擊 &lt;code&gt;doubleClick()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 在指定位置雙擊&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;doubleClick(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;註解&lt;/strong&gt;&lt;br&gt;
也有 &lt;code&gt;tripleClick()&lt;/code&gt; 函數，用以點擊三次螢幕。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="右鍵點擊-rightclick"&gt;右鍵點擊 &lt;code&gt;rightClick()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rightClick(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="中鍵點擊-middleclick"&gt;中鍵點擊 &lt;code&gt;middleClick()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;middleClick(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="分開按下與放開-mousedown--mouseup"&gt;分開按下與放開 &lt;code&gt;mouseDown()&lt;/code&gt; / &lt;code&gt;mouseUp()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按下與放開皆預設為左鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mouseDown() &lt;span style="color:#75715e"&gt;# 按下左鍵（不放開）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mouseUp() &lt;span style="color:#75715e"&gt;# 放開左鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按下右鍵（不放開）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mouseDown(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;right&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 放開右鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;mouseUp(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;right&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="7-滑鼠移動"&gt;7. 滑鼠移動
&lt;/h3&gt;&lt;h4 id="移動到絕對座標-movetox-y-durationnum_seconds"&gt;移動到絕對座標 &lt;code&gt;moveTo(x, y, duration=num_seconds)&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 移動到螢幕座標 (500, 300)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 移動到 (500, 300)，花費 1 秒（加入緩動效果）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 使用緩動函式（easing），讓動作更自然&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, tween&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;easeInOutQuad)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="移動到相對座標-moverelxoffset-yoffset-durationnum_seconds"&gt;移動到相對座標 &lt;code&gt;moveRel(xOffset, yOffset, duration=num_seconds)&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 從目前位置，向右移動 100px、向下移動 50px&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveRel(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# moveRel 與 move 完全相同&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;move(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="拖曳到絕對座標-dragtox-y-durationnum_seconds"&gt;拖曳到絕對座標 &lt;code&gt;dragTo(x, y, duration=num_seconds)&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 從目前位置拖曳到 (700, 400)，花費 0.5 秒&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dragTo(&lt;span style="color:#ae81ff"&gt;700&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 指定使用左鍵拖曳&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dragTo(&lt;span style="color:#ae81ff"&gt;700&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;, button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="拖曳到相對座標-dragrelxoffset-yoffset-durationnum_seconds"&gt;拖曳到相對座標 &lt;code&gt;dragRel(xOffset, yOffset, duration=num_seconds)&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 從目前位置向右拖曳 200px、向下 100px&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dragRel(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# drag 與 dragRel 完全相同&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;drag(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;實用範例：拖曳視窗&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 點擊視窗標題列(假設(400, 30)有視窗標題)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 拖曳視窗到新位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dragTo(&lt;span style="color:#ae81ff"&gt;800&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;常用 tween 緩動函式：&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函式&lt;/th&gt;
&lt;th&gt;效果&lt;/th&gt;
&lt;th&gt;範例指令&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pyautogui.linear&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;等速移動（預設）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pyautogui.moveTo(x, y, tween=pyautogui.linear)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pyautogui.easeInQuad&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;由慢到快&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pyautogui.moveTo(x, y, tween=pyautogui.easeInQuad)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pyautogui.easeOutQuad&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;由快到慢&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pyautogui.moveTo(x, y, tween=pyautogui.easeOutQuad)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pyautogui.easeInOutQuad&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;先慢後快再慢&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pyautogui.moveTo(x, y, tween=pyautogui.easeInOutQuad)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pyautogui.easeInBounce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;到達目標後反彈&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pyautogui.moveTo(x, y, tween=pyautogui.easeInBounce)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id="8-滾輪操作"&gt;8. 滾輪操作
&lt;/h3&gt;&lt;h4 id="垂直滾動-scroll"&gt;垂直滾動 &lt;code&gt;scroll()&lt;/code&gt;
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 向上滾動 3 格（正值向上，負值向下）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;scroll(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 在指定座標向下滾動 5 格&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;scroll(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, x&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, y&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="水平滾動-hscroll"&gt;水平滾動 &lt;code&gt;hscroll()&lt;/code&gt;
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 向右水平滾動 3 格&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hscroll(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 向左水平滾動 3 格&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hscroll(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：滑鼠畫正方形&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：使用 &lt;code&gt;moveTo()&lt;/code&gt; 讓滑鼠依序移動到四個角落，畫出一個邊長 200px 的正方形路徑（從座標 (200, 200) 出發）。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;PAUSE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;正方形路徑完成！&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：自動右鍵選單&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：在桌面空白處模擬右鍵點擊，然後按下 &lt;code&gt;Escape&lt;/code&gt; 關閉選單（需搭配第二篇的鍵盤操作）。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 給自己時間切換到桌面&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 在桌面中央右鍵點擊&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;screen_w, screen_h &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;rightClick(screen_w &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, screen_h &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按 Escape 關閉選單&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;escape&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：計算並移動到螢幕中心&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：用程式取得螢幕解析度後，計算出螢幕中心點，並將滑鼠移動過去。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;width, height &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;center_x &lt;span style="color:#f92672"&gt;=&lt;/span&gt; width &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;center_y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; height &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;螢幕中心：(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;center_x&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;center_y&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;moveTo(center_x, center_y, duration&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://pyautogui.readthedocs.io/en/latest/" target="_blank" rel="noopener"
&gt;PyAutoGUI 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/asweigart/pyautogui" target="_blank" rel="noopener"
&gt;PyAutoGUI GitHub 原始碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://automatetheboringstuff.com/2e/chapter20/" target="_blank" rel="noopener"
&gt;Al Sweigart 的 PyAutoGUI 教學（英文）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>學 FastAPI 前你需要知道的事：HTTP 與 API 基礎</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-http-and-api/</link><pubDate>Mon, 27 Apr 2026 10:32:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-http-and-api/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e4%bb%80%e9%ba%bc%e6%98%af-api" &gt;什麼是 API？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-http-%e5%8d%94%e5%ae%9a%e8%ab%8b%e6%b1%82%e8%88%87%e5%9b%9e%e6%87%89" &gt;HTTP 協定：請求與回應&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-http-%e6%96%b9%e6%b3%95method" &gt;HTTP 方法（Method）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-http-%e7%8b%80%e6%85%8b%e7%a2%bc" &gt;HTTP 狀態碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e4%bb%80%e9%ba%bc%e6%98%af-json" &gt;什麼是 JSON？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%be%9e%e7%80%8f%e8%a6%bd%e5%99%a8%e5%88%b0-api%e5%ae%8c%e6%95%b4%e6%b5%81%e7%a8%8b%e5%9b%9e%e9%a1%a7" &gt;從瀏覽器到 API：完整流程回顧&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-fastapi-%e7%9a%84%e5%88%86%e5%b7%a5%e6%a1%86%e6%9e%b6-vs-%e4%bc%ba%e6%9c%8d%e5%99%a8" &gt;FastAPI 的分工：框架 vs 伺服器&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-什麼是-api"&gt;1. 什麼是 API？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;API（Application Programming Interface，應用程式介面）&lt;/strong&gt; 是不同軟體之間溝通的「橋樑」。&lt;/p&gt;
&lt;p&gt;用生活中的例子來說明：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想像去餐廳吃飯。&lt;br&gt;
&lt;strong&gt;你&lt;/strong&gt;（使用者）不需要知道廚房裡怎麼煮菜，只需要看著&lt;strong&gt;菜單&lt;/strong&gt;（API 文件），跟&lt;strong&gt;服務員&lt;/strong&gt;（API）說「我要一份炒飯」（發送請求），廚房處理好後，服務員再把菜端出來（回傳結果）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在軟體世界裡：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;你的程式&lt;/strong&gt; = 客戶端（Client）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;菜單&lt;/strong&gt; = API 定義的格式與規則&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;廚房&lt;/strong&gt; = 伺服器（Server）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Web API&lt;/strong&gt; 就是透過網路（HTTP）來溝通的 API，是現在最主流的形式。手機 App 打開時載入的資料、天氣網站顯示的即時溫度，背後都是透過 Web API 取得的。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-http-協定請求與回應"&gt;2. HTTP 協定：請求與回應
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;HTTP（HyperText Transfer Protocol，超文本傳輸協定）&lt;/strong&gt; 是網際網路資料傳輸的「語言規則」，規定了客戶端與伺服器之間「怎麼說話」。&lt;/p&gt;
&lt;p&gt;HTTP 的溝通模式只有一種：&lt;strong&gt;「一問一答」&lt;/strong&gt;。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;客戶端（你的程式） ──── 請求（Request） ────▶ 伺服器
客戶端（你的程式） ◀─── 回應（Response） ───── 伺服器
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="一個-http-請求包含什麼"&gt;一個 HTTP 請求包含什麼？
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;組成部分&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;th&gt;範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;URL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;要存取的資源地址&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://api.example.com/users/1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;要做什麼操作&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;（取得）、&lt;code&gt;POST&lt;/code&gt;（新增）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;附加的元資訊（誰在發？帶什麼格式？）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Body&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;夾帶的資料內容（非所有請求都有）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&amp;quot;name&amp;quot;: &amp;quot;小明&amp;quot;, &amp;quot;age&amp;quot;: 25}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="一個-http-回應包含什麼"&gt;一個 HTTP 回應包含什麼？
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;組成部分&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;th&gt;範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Status Code&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;請求的處理結果（成功？失敗？）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;200&lt;/code&gt;、&lt;code&gt;404&lt;/code&gt;、&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;回應的元資訊&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Content-Type: application/json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Body&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;回傳的資料內容&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&amp;quot;id&amp;quot;: 1, &amp;quot;name&amp;quot;: &amp;quot;小明&amp;quot;}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id="3-http-方法method"&gt;3. HTTP 方法（Method）
&lt;/h3&gt;&lt;p&gt;HTTP 方法定義了「要對資源執行什麼動作」。這對應到資料庫操作中常見的 &lt;strong&gt;CRUD（建立、讀取、更新、刪除）&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HTTP 方法&lt;/th&gt;
&lt;th&gt;對應 CRUD&lt;/th&gt;
&lt;th&gt;語意&lt;/th&gt;
&lt;th&gt;範例&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read（讀取）&lt;/td&gt;
&lt;td&gt;取得資源，不改變任何東西&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /users&lt;/code&gt; — 取得所有使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create（建立）&lt;/td&gt;
&lt;td&gt;建立一筆新資源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /users&lt;/code&gt; — 新增一個使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update（完整更新）&lt;/td&gt;
&lt;td&gt;用新資料&lt;strong&gt;完整替換&lt;/strong&gt;一筆資源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PUT /users/1&lt;/code&gt; — 完整更新 id=1 的使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update（部分更新）&lt;/td&gt;
&lt;td&gt;只更新資源中的&lt;strong&gt;特定欄位&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PATCH /users/1&lt;/code&gt; — 只改某個欄位&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete（刪除）&lt;/td&gt;
&lt;td&gt;刪除一筆資源&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DELETE /users/1&lt;/code&gt; — 刪除 id=1 的使用者&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 記憶小技巧&lt;/strong&gt;：&lt;br&gt;
&lt;code&gt;GET&lt;/code&gt; = 取得（不帶 Body）&lt;br&gt;
&lt;code&gt;POST&lt;/code&gt; = 送出（帶 Body，放新資料）&lt;br&gt;
&lt;code&gt;PUT&lt;/code&gt; = 放置（完整取代）&lt;br&gt;
&lt;code&gt;PATCH&lt;/code&gt; = 修補（部分修改）&lt;br&gt;
&lt;code&gt;DELETE&lt;/code&gt; = 刪除&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="4-http-狀態碼"&gt;4. HTTP 狀態碼
&lt;/h3&gt;&lt;p&gt;狀態碼是一個三位數的數字，告訴客戶端「這次請求的結果如何」。&lt;/p&gt;
&lt;h4 id="分類規則第一個數字決定大類"&gt;分類規則（第一個數字決定大類）
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;類別&lt;/th&gt;
&lt;th&gt;範圍&lt;/th&gt;
&lt;th&gt;意義&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;100–199&lt;/td&gt;
&lt;td&gt;資訊性，請求接收中&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;200–299&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;成功&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;300–399&lt;/td&gt;
&lt;td&gt;重新導向&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;400–499&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;客戶端的錯誤&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5xx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500–599&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;伺服器端的錯誤&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="fastapi-開發最常見的狀態碼"&gt;FastAPI 開發最常見的狀態碼
&lt;/h4&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;狀態碼&lt;/th&gt;
&lt;th&gt;名稱&lt;/th&gt;
&lt;th&gt;使用時機&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;成功&lt;/td&gt;
&lt;td&gt;最常見，GET 成功回傳資料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;已建立&lt;/td&gt;
&lt;td&gt;POST 成功新增一筆資料&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;204 No Content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;無內容&lt;/td&gt;
&lt;td&gt;成功但不回傳任何資料（常用於 DELETE）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;錯誤請求&lt;/td&gt;
&lt;td&gt;客戶端傳來的資料格式有問題&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;未授權&lt;/td&gt;
&lt;td&gt;沒有提供身份驗證（如 Token）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;403 Forbidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;禁止存取&lt;/td&gt;
&lt;td&gt;有驗證但&lt;strong&gt;沒有權限&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;找不到&lt;/td&gt;
&lt;td&gt;要存取的資源不存在&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;422 Unprocessable Entity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;無法處理&lt;/td&gt;
&lt;td&gt;資料格式正確但內容驗證失敗（FastAPI 常用）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;伺服器錯誤&lt;/td&gt;
&lt;td&gt;伺服器端程式碼出錯&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id="5-什麼是-json"&gt;5. 什麼是 JSON？
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;JSON（JavaScript Object Notation）&lt;/strong&gt; 是目前 Web API 最主流的&lt;strong&gt;資料交換格式&lt;/strong&gt;，用來在客戶端與伺服器之間傳遞資料。&lt;/p&gt;
&lt;h4 id="json-的語法規則"&gt;JSON 的語法規則
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;王小明&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;isStudent&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;score&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;95.5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;hobbies&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;閱讀&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;寫程式&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;台北市&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;district&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;信義區&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;nickname&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;JSON 規則&lt;/th&gt;
&lt;th&gt;說明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;用 &lt;code&gt;{ }&lt;/code&gt; 包住物件&lt;/td&gt;
&lt;td&gt;一組 Key-Value 的集合&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;用 &lt;code&gt;[ ]&lt;/code&gt; 包住陣列&lt;/td&gt;
&lt;td&gt;多筆資料的列表&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key 必須用&lt;strong&gt;雙引號&lt;/strong&gt; &lt;code&gt;&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;quot;name&amp;quot;&lt;/code&gt; ✅，&lt;code&gt;name&lt;/code&gt; ❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value 的類型&lt;/td&gt;
&lt;td&gt;字串、數字、布林（&lt;code&gt;true&lt;/code&gt;/&lt;code&gt;false&lt;/code&gt;）、陣列、物件、&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id="json-與-python-dict-的對照"&gt;JSON 與 Python dict 的對照
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Python dict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;王小明&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;is_student&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# Python 用 True（大寫）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;hobbies&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;閱讀&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;寫程式&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;nickname&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Python 用 None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;王小明&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;is_student&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;hobbies&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;閱讀&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;寫程式&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;nickname&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;FastAPI 會自動把 Python dict 轉換成 JSON 格式回傳，完全不需要手動處理。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="6-從瀏覽器到-api完整流程回顧"&gt;6. 從瀏覽器到 API：完整流程回顧
&lt;/h3&gt;&lt;p&gt;以開啟「天氣 App」為例，理解一次完整的 API 呼叫：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;1. 使用者打開 App
↓
2. App 發送 HTTP 請求
GET https://api.weather.com/current?city=Taipei
Headers: { &amp;#34;Authorization&amp;#34;: &amp;#34;Bearer my-token&amp;#34; }
↓
3. 伺服器接收請求、查詢資料庫
↓
4. 伺服器回傳 HTTP 回應
Status: 200 OK
Body: { &amp;#34;city&amp;#34;: &amp;#34;台北&amp;#34;, &amp;#34;temperature&amp;#34;: 28, &amp;#34;humidity&amp;#34;: 75 }
↓
5. App 解析 JSON，顯示在畫面上
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id="7-fastapi-的分工框架-vs-伺服器"&gt;7. FastAPI 的分工：框架 vs 伺服器
&lt;/h3&gt;&lt;p&gt;在開始使用 FastAPI 之前，要先理解它和 Uvicorn 的分工：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;使用者的瀏覽器 / App
↓ HTTP 請求
┌─────────────┐
│ Uvicorn │ ← 負責接收網路連線、處理 HTTP 協定
└──────┬──────┘
↓ 轉交給
┌─────────────┐
│ FastAPI │ ← 負責路由分配、資料驗證、執行你的程式邏輯
└──────┬──────┘
↓ 執行
┌─────────────┐
│ 你的程式碼 │ ← def read_item(), def create_user() ...
└─────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;FastAPI&lt;/strong&gt;：決定「收到這個 URL 要做什麼事」——這是你寫的邏輯&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Uvicorn&lt;/strong&gt;：負責在網路上監聽、收發 HTTP 封包——這是底層基礎設施&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：HTTP 方法配對&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：請說明以下 API 端點應該使用哪個 HTTP 方法？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;取得 id=5 的商品資訊&lt;/li&gt;
&lt;li&gt;新增一筆訂單&lt;/li&gt;
&lt;li&gt;刪除 id=3 的留言&lt;/li&gt;
&lt;li&gt;更新使用者的電子郵件（只改一個欄位）&lt;/li&gt;
&lt;li&gt;完整替換一篇文章的所有內容&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /products/5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /orders&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE /comments/3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PATCH /users/{id}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT /articles/{id}&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：狀態碼判讀&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：以下情境應該回傳哪個 HTTP 狀態碼？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用者輸入了帳號密碼，但帳號不存在&lt;/li&gt;
&lt;li&gt;成功建立了一筆新資料&lt;/li&gt;
&lt;li&gt;伺服器程式碼發生了 Python 例外（Exception）&lt;/li&gt;
&lt;li&gt;請求需要登入，但沒有提供 Token&lt;/li&gt;
&lt;li&gt;請求格式正確，但 email 欄位不符合 email 格式&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;404 Not Found&lt;/code&gt;（找不到此帳號）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;500 Internal Server Error&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;401 Unauthorized&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;422 Unprocessable Entity&lt;/code&gt;（FastAPI 的資料驗證失敗）&lt;/li&gt;
&lt;/ol&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：JSON 轉 Python dict&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：將以下 JSON 轉換為 Python dict：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;is_active&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;bio&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;is_active&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, &lt;span style="color:#75715e"&gt;# JSON true → Python True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;admin&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;bio&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#75715e"&gt;# JSON null → Python None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：&lt;code&gt;true&lt;/code&gt; → &lt;code&gt;True&lt;/code&gt;，&lt;code&gt;false&lt;/code&gt; → &lt;code&gt;False&lt;/code&gt;，&lt;code&gt;null&lt;/code&gt; → &lt;code&gt;None&lt;/code&gt;&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
---
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Overview" target="_blank" rel="noopener"
&gt;MDN Web Docs - HTTP 概觀&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status" target="_blank" rel="noopener"
&gt;MDN Web Docs - HTTP 狀態碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.mozilla.org/zh-TW/docs/Learn/JavaScript/Objects/JSON" target="_blank" rel="noopener"
&gt;MDN Web Docs - JSON 介紹&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 使用者指南&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>如何使用 FastAPI</title><link>https://Dandelionlibra.github.io/2026/04/how-to-use-fastapi/</link><pubDate>Sun, 26 Apr 2026 22:15:56 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/how-to-use-fastapi/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-fastapi-%e5%9f%ba%e6%9c%ac%e4%bb%8b%e7%b4%b9" &gt;FastAPI 基本介紹&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%ae%89%e8%a3%9d-fastapi-%e8%88%87-uvicorn" &gt;安裝 FastAPI 與 Uvicorn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e5%80%8b-fastapi-%e6%87%89%e7%94%a8%e7%a8%8b%e5%bc%8f" &gt;建立第一個 FastAPI 應用程式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%95%9f%e5%8b%95%e4%bc%ba%e6%9c%8d%e5%99%a8%e8%88%87%e6%b8%ac%e8%a9%a6" &gt;啟動伺服器與測試&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e8%87%aa%e5%8b%95%e7%94%a2%e7%94%9f-api-%e6%96%87%e4%bb%b6" &gt;自動產生 API 文件&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-fastapi-基本介紹"&gt;1. FastAPI 基本介紹
&lt;/h3&gt;&lt;p&gt;FastAPI 是一個現代、快速（高效能）的 Web 框架，用於建構 API，基於標準 Python 類型提示。它的主要特色包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;高效能&lt;/strong&gt;：可與 NodeJS 和 Go 相提並論的極高網頁效能。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;易於使用&lt;/strong&gt;：自動產生互動式的 API 文件 (Swagger UI)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;減少錯誤&lt;/strong&gt;：透過資料驗證機制，減少人為錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-安裝-fastapi-與-uvicorn"&gt;2. 安裝 FastAPI 與 Uvicorn
&lt;/h3&gt;&lt;p&gt;在開始之前，請確保環境中已安裝 Python。我們需要安裝 FastAPI 框架本身，以及一個 ASGI 伺服器 (如 Uvicorn) 來運行它。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 補充說明：什麼是 Uvicorn？&lt;/strong&gt;&lt;br&gt;
FastAPI 是一個「網頁框架」，負責處理程式邏輯與路由；但它本身並沒有內建處理網路連線的伺服器功能。
因此，我們需要搭配 &lt;strong&gt;Uvicorn&lt;/strong&gt; 這個超高效能的 ASGI 伺服器。它就像是一座橋樑，負責在網路上接收使用者的 HTTP 請求，轉交給 FastAPI 處理後，再把結果傳回給瀏覽器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;請在終端機輸入以下指令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install fastapi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install &lt;span style="color:#e6db74"&gt;&amp;#34;uvicorn[standard]&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3-建立第一個-fastapi-應用程式"&gt;3. 建立第一個 FastAPI 應用程式
&lt;/h3&gt;&lt;p&gt;建立一個名為 &lt;code&gt;main.py&lt;/code&gt; 的檔案，並輸入以下程式碼：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_root&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;World&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_item&lt;/span&gt;(item_id: int, q: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這段程式碼定義了兩個端點 (Endpoints)：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根目錄 &lt;code&gt;/&lt;/code&gt;：回傳一個簡單的 JSON &lt;code&gt;{ &amp;quot;Hello&amp;quot;: &amp;quot;World&amp;quot; }&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/items/{item_id}&lt;/code&gt;：展示了如何接收路徑參數 (&lt;code&gt;item_id&lt;/code&gt;) 以及查詢參數 (&lt;code&gt;q&lt;/code&gt;)。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-啟動伺服器與測試"&gt;4. 啟動伺服器與測試
&lt;/h3&gt;&lt;p&gt;在終端機中，確保處於 &lt;code&gt;main.py&lt;/code&gt; 所在的目錄，執行以下指令來啟動伺服器：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uvicorn main:app --reload
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt;：指的是 &lt;code&gt;main.py&lt;/code&gt; 檔案。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app&lt;/code&gt;：是 &lt;code&gt;main.py&lt;/code&gt; 中 &lt;code&gt;app = FastAPI()&lt;/code&gt; 建立的實例。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--reload&lt;/code&gt;：當程式碼變動時會自動重新載入，非常適合在開發過程中使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;啟動後，可以在瀏覽器中造訪：&lt;a class="link" href="http://127.0.0.1:8000" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000&lt;/a&gt;
將會看到：&lt;code&gt;{&amp;quot;Hello&amp;quot;: &amp;quot;World&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="5-自動產生-api-文件"&gt;5. 自動產生 API 文件
&lt;/h3&gt;&lt;p&gt;FastAPI 最大的亮點之一就是會自動產生文件。
在伺服器運行時，可以造訪：&lt;/p&gt;
&lt;h4 id="1-swagger-ui-互動式文件"&gt;1. Swagger UI (互動式文件)
&lt;/h4&gt;&lt;p&gt;造訪：&lt;a class="link" href="http://127.0.0.1:8000/docs" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000/docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何閱讀與使用 Swagger UI？&lt;/strong&gt;&lt;br&gt;
打開網頁後，會看到剛剛寫好的 &lt;code&gt;/&lt;/code&gt; 與 &lt;code&gt;/items/{item_id}&lt;/code&gt; 兩個端點 (Endpoints)：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;展開端點&lt;/strong&gt;：點擊想測試的端點（例如 &lt;code&gt;/items/{item_id}&lt;/code&gt;），它會往下展開顯示詳細資訊。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;開始測試&lt;/strong&gt;：點擊右上角的 &lt;strong&gt;「Try it out」&lt;/strong&gt; 按鈕。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;輸入參數&lt;/strong&gt;：這時參數欄位會變成可輸入狀態。可以在 &lt;code&gt;item_id&lt;/code&gt; 填入數字（例如 &lt;code&gt;5&lt;/code&gt;），在 &lt;code&gt;q&lt;/code&gt; 填入字串（例如 &lt;code&gt;somequery&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;執行請求&lt;/strong&gt;：點擊下方的 &lt;strong&gt;「Execute」&lt;/strong&gt; 藍色按鈕。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;查看結果&lt;/strong&gt;：往下捲動到 &lt;strong&gt;「Responses」&lt;/strong&gt; 區塊，就可以看到伺服器回傳的真實 JSON 資料與 HTTP 狀態碼（例如 &lt;code&gt;200&lt;/code&gt; 表示成功）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="2-redoc-靜態文件"&gt;2. ReDoc (靜態文件)
&lt;/h4&gt;&lt;p&gt;造訪：&lt;a class="link" href="http://127.0.0.1:8000/redoc" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000/redoc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;這是另一種風格的 API 文件。它的排版更適合當作純閱讀的參考手冊，不包含互動測試的功能。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;這樣就成功建立並運行了一個 FastAPI 應用程式！&lt;/p&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：新增一個路由&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：在 &lt;code&gt;main.py&lt;/code&gt; 中新增一個 &lt;code&gt;GET /hello/{name}&lt;/code&gt; 的端點，讓它回傳 &lt;code&gt;{&amp;quot;message&amp;quot;: &amp;quot;Hello, {name}!&amp;quot;}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;例如：呼叫 &lt;code&gt;GET /hello/Alice&lt;/code&gt; 應該回傳 &lt;code&gt;{&amp;quot;message&amp;quot;: &amp;quot;Hello, Alice!&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/hello/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;say_hello&lt;/span&gt;(name: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Hello, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：Swagger UI 操作&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：啟動伺服器後，請用 Swagger UI 測試以下兩個端點，並記錄回傳的結果：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /&lt;/code&gt; — 預期回傳什麼？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/42?q=fastapi&lt;/code&gt; — 預期回傳什麼？&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GET /&lt;/code&gt; 回傳：&lt;code&gt;{&amp;quot;Hello&amp;quot;: &amp;quot;World&amp;quot;}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/42?q=fastapi&lt;/code&gt; 回傳：&lt;code&gt;{&amp;quot;item_id&amp;quot;: 42, &amp;quot;q&amp;quot;: &amp;quot;fastapi&amp;quot;}&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;操作步驟：開啟 &lt;a class="link" href="http://127.0.0.1:8000/docs" target="_blank" rel="noopener"
&gt;http://127.0.0.1:8000/docs&lt;/a&gt; → 點擊端點 → Try it out → 輸入參數 → Execute → 查看 Responses&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：傳入錯誤型別會怎樣？&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：在 Swagger UI 中呼叫 &lt;code&gt;GET /items/{item_id}&lt;/code&gt;，但把 &lt;code&gt;item_id&lt;/code&gt; 填入字串 &lt;code&gt;&amp;quot;abc&amp;quot;&lt;/code&gt; 而不是整數，觀察 FastAPI 回傳什麼？&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;p&gt;FastAPI 會自動回傳 &lt;code&gt;422 Unprocessable Entity&lt;/code&gt;，並在 body 中說明哪個欄位驗證失敗：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;int_parsing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be a valid integer, unable to parse string as an integer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這就是 FastAPI 自動驗證型別的威力——完全不需要自己寫驗證邏輯！&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 使用者指南&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>如何使用 venv 建立 Python 虛擬環境</title><link>https://Dandelionlibra.github.io/2026/04/how-to-use-venv/</link><pubDate>Sat, 25 Apr 2026 03:29:13 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/how-to-use-venv/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e4%bb%80%e9%ba%bc%e6%98%af%e8%99%9b%e6%93%ac%e7%92%b0%e5%a2%83%e7%82%ba%e4%bd%95%e9%9c%80%e8%a6%81%e5%ae%83" &gt;什麼是虛擬環境？為何需要它？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%bb%ba%e7%ab%8b%e8%99%9b%e6%93%ac%e7%92%b0%e5%a2%83" &gt;建立虛擬環境&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e9%80%b2%e9%9a%8e%e5%8f%83%e6%95%b8%e6%8c%87%e4%bb%a4-options" &gt;進階參數指令 (Options)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%95%9f%e5%8b%95%e8%88%87%e9%80%80%e5%87%ba%e8%99%9b%e6%93%ac%e7%92%b0%e5%a2%83" &gt;啟動與退出虛擬環境&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e5%a5%97%e4%bb%b6%e7%9a%84%e5%ae%89%e8%a3%9d%e5%8c%af%e5%87%ba%e8%88%87%e6%89%b9%e6%ac%a1%e7%a7%bb%e9%99%a4" &gt;套件的安裝、匯出與批次移除&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%b8%b8%e8%a6%8b%e5%95%8f%e9%a1%8c%e8%88%87%e6%b3%a8%e6%84%8f%e4%ba%8b%e9%a0%85" &gt;常見問題與注意事項&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-什麼是虛擬環境為何需要它"&gt;1. 什麼是虛擬環境？為何需要它？
&lt;/h3&gt;&lt;p&gt;在開發 Python 專案時，經常需要使用 &lt;code&gt;pip&lt;/code&gt; 安裝各種第三方套件。如果不使用虛擬環境，所有套件都會被安裝到系統全域環境中。這樣會產生幾個嚴重問題：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;版本衝突&lt;/strong&gt;：專案 A 需要 &lt;code&gt;Django 3.0&lt;/code&gt;，但專案 B 卻依賴 &lt;code&gt;Django 4.0&lt;/code&gt;，兩者無法共存於同一個環境。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;環境污染&lt;/strong&gt;：全域環境累積過多不需要的套件，難以管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;venv&lt;/code&gt; 是 Python 3.3 之後內建的虛擬環境管理工具，它能為每個專案建立一個獨立的資料夾。在這個資料夾中，擁有獨立的 Python 執行檔與獨立的套件庫，從而完美隔離各個專案。&lt;/p&gt;
&lt;h3 id="2-建立虛擬環境"&gt;2. 建立虛擬環境
&lt;/h3&gt;&lt;p&gt;建立虛擬環境主要有兩種指定路徑的方式。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方式一：在目前目錄下建立（相對路徑）&lt;/strong&gt;
請先開啟終端機，並使用 &lt;code&gt;cd&lt;/code&gt; 指令切換到專案資料夾，接著輸入以下指令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Windows / macOS / Linux 通用指令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m venv venv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;python -m venv&lt;/code&gt;：呼叫 Python 內建的 &lt;code&gt;venv&lt;/code&gt; 模組。&lt;/li&gt;
&lt;li&gt;最後的 &lt;code&gt;venv&lt;/code&gt;：這是即將產生的虛擬環境資料夾名稱。一般約定成俗會將其命名為 &lt;code&gt;venv&lt;/code&gt; 或 &lt;code&gt;.venv&lt;/code&gt;。執行後，目前的專案目錄下就會多出這個資料夾。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;方式二：在指定位置建立（絕對路徑）&lt;/strong&gt;
如果不想將虛擬環境放在專案資料夾內，或者想統一集中管理所有虛擬環境，可以直接指定一個完整的路徑：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python -m venv /path/to/new/virtual/environment
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;兩個指令的差別：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;python -m venv venv&lt;/code&gt;&lt;/strong&gt;：利用&lt;strong&gt;相對路徑&lt;/strong&gt;，將虛擬環境建立在「目前終端機所在的資料夾」底下。適合讓每個專案擁有專屬、跟隨專案代碼的環境，這也是最常見的做法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;python -m venv /path/to/...&lt;/code&gt;&lt;/strong&gt;：利用&lt;strong&gt;絕對路徑&lt;/strong&gt;（或明確指定的路徑），無論目前終端機在哪個目錄，都會將虛擬環境強制建立在指定的特定位置上。適合習慣把所有虛擬環境統一存放在同一個集中目錄（例如 &lt;code&gt;~/.virtualenvs/&lt;/code&gt;）的開發情境。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不管使用哪種方式，執行後該資料夾裡面都會包含獨立的 Python 執行檔與套件庫結構。&lt;/p&gt;
&lt;h3 id="3-進階參數指令-options"&gt;3. 進階參數指令 (Options)
&lt;/h3&gt;&lt;p&gt;在建立虛擬環境時，可以在終端機加上特定的參數來客製化環境設定（可透過 &lt;code&gt;python -m venv -h&lt;/code&gt; 查詢完整列表），以下介紹幾個常用的進階參數：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--system-site-packages&lt;/code&gt;&lt;/strong&gt;：預設情況下，虛擬環境是完全隔離的。加上此參數後，虛擬環境將「可以存取」系統全域 (System-wide) 所安裝的套件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--clear&lt;/code&gt;&lt;/strong&gt;：如果指定的資料夾已經存在，加上此參數會在建立前「先清空」該資料夾裡的所有內容，相當於重新建立一個乾淨的環境。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--without-pip&lt;/code&gt;&lt;/strong&gt;：建立環境時預設會自動安裝 &lt;code&gt;pip&lt;/code&gt;。若加上此參數則會跳過安裝。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--prompt &amp;lt;PROMPT&amp;gt;&lt;/code&gt;&lt;/strong&gt;：自訂啟動虛擬環境後，終端機前面顯示的「提示字元前綴」（預設為資料夾的名稱）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--upgrade-deps&lt;/code&gt;&lt;/strong&gt;：建立環境的同時，將環境內的基礎套件（如 &lt;code&gt;pip&lt;/code&gt; 和 &lt;code&gt;setuptools&lt;/code&gt;）直接升級到 PyPI 上的最新版本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;--without-scm-ignore-files&lt;/code&gt;&lt;/strong&gt;：建立環境時，不自動生成原始碼控制的忽略檔（新版 Python 預設會自動產生 &lt;code&gt;.gitignore&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-啟動與退出虛擬環境"&gt;4. 啟動與退出虛擬環境
&lt;/h3&gt;&lt;p&gt;建立好之後，需要「啟動 (Activate)」它，讓系統知道接下來的操作都要在這個隔離環境內進行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;啟動虛擬環境 (Windows Command Prompt)：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;venv\Scripts\activate.bat
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;啟動虛擬環境 (Windows PowerShell)：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;venv\Scripts\Activate.ps1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;啟動虛擬環境 (macOS / Linux)：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source venv/bin/activate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;啟動成功後，終端機的提示字元前方通常會出現 &lt;code&gt;(venv)&lt;/code&gt;，代表目前已經身處於虛擬環境中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;退出虛擬環境：&lt;/strong&gt;
當開發結束，想要回到系統全域環境時，只需執行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;deactivate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="5-套件的安裝匯出與批次移除"&gt;5. 套件的安裝、匯出與批次移除
&lt;/h3&gt;&lt;p&gt;在虛擬環境啟動的狀態下，使用 &lt;code&gt;pip install&lt;/code&gt; 安裝的任何套件，都會被存放在這個虛擬環境中，完全不會影響到系統。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;安裝套件：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install requests
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;匯出套件清單：&lt;/strong&gt;
為了讓專案能夠在其他電腦上重現相同的環境，會將目前安裝的所有套件名稱與版本匯出成一份清單。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip freeze &amp;gt; requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;從清單還原環境：&lt;/strong&gt;
當其他人拿到專案時，只要先建立並啟動自己的虛擬環境，就可以透過這份清單一次安裝所有必需的套件。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;強制清空環境的所有套件：&lt;/strong&gt;
如果環境遭到污染（裝了太多不相關的套件），或者想將目前環境打掉重練，可以利用 &lt;code&gt;pip freeze&lt;/code&gt; 配合 &lt;code&gt;pip uninstall&lt;/code&gt; 來一鍵批次卸載所有第三方套件。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先，將目前所有安裝的套件清單匯出到一個暫存檔：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip freeze &amp;gt; uninstall.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;接著，讓 pip 讀取這個清單，並強制 (&lt;code&gt;-y&lt;/code&gt;) 刪除裡面的所有套件：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip uninstall -r uninstall.txt -y
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;(備註：&lt;code&gt;pip freeze&lt;/code&gt; 預設不會把基礎套件 &lt;code&gt;pip&lt;/code&gt;、&lt;code&gt;setuptools&lt;/code&gt;、&lt;code&gt;wheel&lt;/code&gt; 列入清單，因此不用擔心會刪除到這三個核心套件。)&lt;/em&gt;&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;刪除完成後，再把暫存檔清掉即可：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Windows 命令提示字元 (cmd):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;del uninstall.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Windows PowerShell:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Remove-Item uninstall.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# macOS / Linux:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rm uninstall.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="6-常見問題與注意事項"&gt;6. 常見問題與注意事項
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不要將 venv 資料夾加入版本控制&lt;/strong&gt;：虛擬環境資料夾體積龐大且包含系統特定的編譯檔案。請務必在 &lt;code&gt;.gitignore&lt;/code&gt; 檔案中加入 &lt;code&gt;venv/&lt;/code&gt;，避免將它推送到 GitHub。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PowerShell 執行原則錯誤&lt;/strong&gt;：在 Windows PowerShell 啟動時如果遇到「執行原則不允許」的紅色錯誤，請以系統管理員身分開啟 PowerShell 並執行 &lt;code&gt;Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser&lt;/code&gt; 即可解決。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="參考資料"&gt;參考資料
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.python.org/zh-tw/3/library/venv.html" target="_blank" rel="noopener"
&gt;Python 官方文件：venv — 建立虛擬環境&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>如何使用 PyAutoGUI（二）：鍵盤控制</title><link>https://Dandelionlibra.github.io/2026/05/how-to-use-pyautogui-keyboard/</link><pubDate>Fri, 01 May 2026 19:20:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/05/how-to-use-pyautogui-keyboard/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e6%96%87%e5%ad%97%e8%bc%b8%e5%85%a5" &gt;文字輸入&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%96%ae%e9%8d%b5%e6%93%8d%e4%bd%9c" &gt;單鍵操作&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e6%8c%89%e4%b8%8b%e4%b8%a6%e6%94%be%e9%96%8b%e5%96%ae%e4%b8%80%e6%8c%89%e9%8d%b5-press" &gt;按下並放開單一按鍵&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e6%8c%89%e4%bd%8f%e4%b8%8d%e6%94%be%e8%88%87%e6%94%be%e9%96%8b%e6%8c%89%e9%8d%b5-keydown--keyup" &gt;按住不放與放開按鍵&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e4%b8%8a%e4%b8%8b%e6%96%87%e7%ae%a1%e7%90%86%e5%99%a8-hold" &gt;上下文管理器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e7%b5%84%e5%90%88%e9%8d%b5%e8%88%87%e7%86%b1%e9%8d%b5-hotkey" &gt;組合鍵與熱鍵&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%b8%b8%e7%94%a8-key-%e5%90%8d%e7%a8%b1%e5%b0%8d%e7%85%a7%e8%a1%a8" &gt;常用 Key 名稱對照表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e8%a8%8a%e6%81%af%e5%bd%88%e5%87%ba%e8%a6%96%e7%aa%97" &gt;訊息彈出視窗&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e9%a1%af%e7%a4%ba%e8%a8%8a%e6%81%af-alert" &gt;alert()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e7%a2%ba%e8%aa%8d%e8%a6%96%e7%aa%97-confirm" &gt;confirm()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e6%96%87%e5%ad%97%e8%bc%b8%e5%85%a5%e8%a6%96%e7%aa%97-prompt" &gt;prompt()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%af%86%e7%a2%bc%e8%bc%b8%e5%85%a5%e8%a6%96%e7%aa%97-password-%e9%81%ae%e8%94%bd%e9%a1%af%e7%a4%ba" &gt;password()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e5%af%a6%e7%94%a8%e7%af%84%e4%be%8b%e8%87%aa%e5%8b%95%e9%96%8b%e5%95%9f%e8%a8%98%e4%ba%8b%e6%9c%ac%e4%b8%a6%e8%bc%b8%e5%85%a5%e6%96%87%e5%ad%97" &gt;實用範例：自動開啟記事本並輸入文字&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-文字輸入"&gt;1. 文字輸入
&lt;/h3&gt;&lt;h4 id="typewrite--write"&gt;&lt;code&gt;typewrite()&lt;/code&gt; / &lt;code&gt;write()&lt;/code&gt;
&lt;/h4&gt;&lt;p&gt;兩者完全相同，用於模擬逐鍵輸入文字：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 在目前焦點位置輸入文字&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;Hello, World!&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 設定每個字元之間的間隔（秒），模擬真實打字速度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;Hello!&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# write() 與 typewrite() 完全相同&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;write(&lt;span style="color:#e6db74"&gt;&amp;#39;Python automation&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.05&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 注意：中文輸入限制&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;typewrite()&lt;/code&gt; 只支援 ASCII 字元，&lt;strong&gt;無法直接輸入中文&lt;/strong&gt;。若要輸入中文，需改用剪貼簿方式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyperclip &lt;span style="color:#75715e"&gt;# pip install pyperclip&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 將中文複製到剪貼簿，再貼上&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyperclip&lt;span style="color:#f92672"&gt;.&lt;/span&gt;copy(&lt;span style="color:#e6db74"&gt;&amp;#39;你好，世界！&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;v&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 稍後會介紹 hotkey 函數&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="2-單鍵操作"&gt;2. 單鍵操作：
&lt;/h3&gt;&lt;h4 id="按下並放開單一按鍵-press"&gt;按下並放開單一按鍵 &lt;code&gt;press()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按下 Enter 鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;enter&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按下 Tab 鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;tab&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按下方向鍵（上下左右）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;down&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;right&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 連續按多次（按 3 次空白鍵）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;space&amp;#39;&lt;/span&gt;, presses&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 設定每次按鍵之間的間隔&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;tab&amp;#39;&lt;/span&gt;, presses&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 也可以傳入 list，依序按下多個鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press([&lt;span style="color:#e6db74"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;up&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;down&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;down&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;right&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="按住不放與放開按鍵-keydown--keyup"&gt;按住不放與放開按鍵 &lt;code&gt;keyDown()&lt;/code&gt; / &lt;code&gt;keyUp()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按住 Shift 鍵不放&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;keyDown(&lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 輸入文字（若無鎖定`CAPSLOCK`，此時會全部變大寫）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 實際輸入 HELLO&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 放開 Shift 鍵&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;keyUp(&lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;實際應用：按住 Shift 並點擊以選取文字&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 先點擊起始位置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 按住 Shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;keyDown(&lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 點擊結束位置（選取這段範圍的文字）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(&lt;span style="color:#ae81ff"&gt;600&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 放開 Shift&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;keyUp(&lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;h4 id="上下文管理器-hold"&gt;上下文管理器 &lt;code&gt;hold()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 該shift鍵將在上下文區塊期間保持按住狀態。&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hold(&lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press([&lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;left&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="組合鍵與熱鍵-hotkey"&gt;組合鍵與熱鍵 &lt;code&gt;hotkey()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# Ctrl + C（複製）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;v&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# Ctrl + V（貼上）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Alt + F4（關閉視窗，Windows）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;alt&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;f4&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Ctrl + Alt + Delete（Windows）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;alt&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;delete&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# macOS 截圖：Cmd + Shift + 4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;command&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;4&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Ctrl + Shift + T（重開分頁，瀏覽器）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;t&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="3-常用-key-名稱對照表"&gt;3. 常用 Key 名稱對照表
&lt;/h3&gt;&lt;p&gt;以下是 &lt;code&gt;press()&lt;/code&gt;、&lt;code&gt;keyDown()&lt;/code&gt;、&lt;code&gt;keyUp()&lt;/code&gt;、&lt;code&gt;hotkey()&lt;/code&gt; 可接受的常用鍵名：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;類別&lt;/th&gt;
&lt;th&gt;Key 名稱&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;修飾鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;shift&lt;/code&gt;、&lt;code&gt;ctrl&lt;/code&gt;、&lt;code&gt;alt&lt;/code&gt;、&lt;code&gt;command&lt;/code&gt;（macOS）、&lt;code&gt;win&lt;/code&gt;（Windows）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;功能鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;f1&lt;/code&gt; ~ &lt;code&gt;f12&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;方向鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;up&lt;/code&gt;、&lt;code&gt;down&lt;/code&gt;、&lt;code&gt;left&lt;/code&gt;、&lt;code&gt;right&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;特殊鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;enter&lt;/code&gt;、&lt;code&gt;esc&lt;/code&gt;、&lt;code&gt;tab&lt;/code&gt;、&lt;code&gt;backspace&lt;/code&gt;、&lt;code&gt;delete&lt;/code&gt;、&lt;code&gt;space&lt;/code&gt;、&lt;code&gt;capslock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page 鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pageup&lt;/code&gt;、&lt;code&gt;pagedown&lt;/code&gt;、&lt;code&gt;home&lt;/code&gt;、&lt;code&gt;end&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;數字鍵盤&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;num0&lt;/code&gt; ~ &lt;code&gt;num9&lt;/code&gt;、&lt;code&gt;numlock&lt;/code&gt;、&lt;code&gt;add&lt;/code&gt;、&lt;code&gt;subtract&lt;/code&gt;、&lt;code&gt;multiply&lt;/code&gt;、&lt;code&gt;divide&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;媒體鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;volumemute&lt;/code&gt;、&lt;code&gt;volumedown&lt;/code&gt;、&lt;code&gt;volumeup&lt;/code&gt;、&lt;code&gt;playpause&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;符號鍵&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;!&lt;/code&gt;、&lt;code&gt;@&lt;/code&gt;、&lt;code&gt;#&lt;/code&gt;、&lt;code&gt;$&lt;/code&gt;、&lt;code&gt;%&lt;/code&gt;、&lt;code&gt;^&lt;/code&gt;、&lt;code&gt;&amp;amp;&lt;/code&gt;、&lt;code&gt;*&lt;/code&gt;、&lt;code&gt;(&lt;/code&gt;、&lt;code&gt;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;字母/數字&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;～&lt;code&gt;z&lt;/code&gt;、&lt;code&gt;0&lt;/code&gt;～&lt;code&gt;9&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;查看所有支援的鍵名：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;KEYBOARD_KEYS)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你也可以使用下方的互動式工具，快速搜尋按鍵名稱與對應的變數寫法：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;互動小工具：鍵盤按鍵對照查詢&lt;/strong&gt;
支援分類篩選與關鍵字搜尋，點擊按鍵卡片可產生範例程式碼。&lt;br&gt;
程式碼已開源於：&lt;a class="link" href="https://dandelionlibra.github.io/tools/pynput_key_reference.html" target="_blank" rel="noopener"
&gt;pynput-key-reference&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;iframe src="https://dandelionlibra.github.io/tools/pynput_key_reference.html" width="100%" height="500px" style="border: 1px solid rgba(255,255,255,0.1); border-radius: 8px; background: #0d0d0f;"&gt;&lt;/iframe&gt;
&lt;hr&gt;
&lt;h3 id="4-訊息彈出視窗"&gt;4. 訊息彈出視窗
&lt;/h3&gt;&lt;p&gt;PyAutoGUI 提供了簡易的 GUI 彈出視窗函式，可在自動化流程中提示使用者或取得確認。&lt;/p&gt;
&lt;h4 id="顯示訊息-alert"&gt;顯示訊息 &lt;code&gt;alert()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 顯示提示訊息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alert(&lt;span style="color:#e6db74"&gt;&amp;#39;操作完成！&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 自訂標題&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alert(text&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;備份成功。&amp;#39;&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;通知&amp;#39;&lt;/span&gt;, button&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;確定&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="確認視窗-confirm"&gt;確認視窗 &lt;code&gt;confirm()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;confirm(&lt;span style="color:#e6db74"&gt;&amp;#39;確定要刪除嗎？&amp;#39;&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;確認&amp;#39;&lt;/span&gt;, buttons&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;確定&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;取消&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(result) &lt;span style="color:#75715e"&gt;# 使用者點了哪個按鈕的文字&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="文字輸入視窗-prompt"&gt;文字輸入視窗 &lt;code&gt;prompt()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;prompt(&lt;span style="color:#e6db74"&gt;&amp;#39;請輸入你的名字：&amp;#39;&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;輸入&amp;#39;&lt;/span&gt;, default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; name:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;你好，&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;name&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;！&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="密碼輸入視窗-password遮蔽顯示"&gt;密碼輸入視窗 &lt;code&gt;password()&lt;/code&gt;（遮蔽顯示）：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pwd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;password(&lt;span style="color:#e6db74"&gt;&amp;#39;請輸入密碼：&amp;#39;&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;登入&amp;#39;&lt;/span&gt;, default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, mask&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;輸入的密碼：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;pwd&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="5-實用範例自動開啟記事本並輸入文字"&gt;5. 實用範例：自動開啟記事本並輸入文字
&lt;/h3&gt;&lt;p&gt;以下範例示範如何在 Windows 上自動開啟記事本，並輸入一段文字再儲存：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; subprocess
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 開啟記事本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;subprocess&lt;span style="color:#f92672"&gt;.&lt;/span&gt;Popen([&lt;span style="color:#e6db74"&gt;&amp;#39;notepad.exe&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;1.5&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 等待視窗開啟&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;shift&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 切換鍵盤成英文&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 輸入文字&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;Hello from PyAutoGUI!&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.05&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;enter&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;This is automated typing.&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.05&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 儲存（Ctrl + S）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 在另存新檔視窗中輸入檔名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;auto_test.txt&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.05&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;enter&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;完成！已儲存為 auto_test.txt&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：自動全選並複製&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：在任意文字編輯器中，用 PyAutoGUI 執行「全選→複製→游標移至末尾→貼上」的操作序列。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 手動切換到目標視窗&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 全選&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;c&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 複製&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;end&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 移至末尾&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;enter&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 換行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;hotkey(&lt;span style="color:#e6db74"&gt;&amp;#39;ctrl&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;v&amp;#39;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 貼上&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：詢問使用者再執行&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：用 &lt;code&gt;confirm()&lt;/code&gt; 彈出確認視窗，若使用者點「確定」，才執行自動輸入文字的動作；若點「取消」，則印出「已取消」。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;confirm(&lt;span style="color:#e6db74"&gt;&amp;#39;是否要開始自動輸入？&amp;#39;&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;確認&amp;#39;&lt;/span&gt;, buttons&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;確定&amp;#39;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#39;取消&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; result &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;確定&amp;#39;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 切換到目標視窗&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;自動輸入的文字！&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#39;已取消&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：模擬 Vim 儲存並離開&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：假設 Vim 已開啟並在 Normal mode，用 PyAutoGUI 模擬輸入 &lt;code&gt;:wq&lt;/code&gt; 後按 Enter 來儲存並離開。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 切換到 Vim 視窗&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 確保在 Normal mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;esc&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 輸入 :wq 指令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;:wq&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;enter&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://pyautogui.readthedocs.io/en/latest/keyboard.html" target="_blank" rel="noopener"
&gt;PyAutoGUI 官方文件 — Keyboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pyautogui.readthedocs.io/en/latest/msgbox.html" target="_blank" rel="noopener"
&gt;PyAutoGUI 官方文件 — Message Box&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/asweigart/pyautogui" target="_blank" rel="noopener"
&gt;PyAutoGUI GitHub 原始碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://automatetheboringstuff.com/2e/chapter20/" target="_blank" rel="noopener"
&gt;Automate the Boring Stuff with Python — Chapter 20&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>如何使用 PyAutoGUI（三）：螢幕截圖與圖像辨識</title><link>https://Dandelionlibra.github.io/2026/05/how-to-use-pyautogui-screenshot/</link><pubDate>Sat, 02 May 2026 21:40:44 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/05/how-to-use-pyautogui-screenshot/</guid><description>&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e6%88%aa%e5%8f%96%e8%9e%a2%e5%b9%95%e7%95%ab%e9%9d%a2" &gt;截取螢幕畫面&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%85%a8%e8%9e%a2%e5%b9%95%e6%88%aa%e5%9c%96-screenshot" &gt;全螢幕截圖&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%8d%80%e5%9f%9f%e6%88%aa%e5%9c%96-screenshotregion" &gt;區域截圖&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%9c%96%e5%83%8f%e8%be%a8%e8%ad%98%e5%9c%a8%e8%9e%a2%e5%b9%95%e4%b8%8a%e5%b0%8b%e6%89%be%e5%9c%96%e7%89%87" &gt;圖像辨識：在螢幕上尋找圖片&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%b0%8b%e6%89%be%e5%9c%96%e7%89%87%e4%bd%8d%e7%bd%ae-locateonscreen" &gt;尋找圖片位置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%8f%96%e5%be%97%e5%9c%96%e7%89%87%e4%b8%ad%e5%bf%83%e5%ba%a7%e6%a8%99-locatecentersonscreen" &gt;取得圖片中心座標&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%b0%8b%e6%89%be%e6%89%80%e6%9c%89%e7%ac%a6%e5%90%88%e4%bd%8d%e7%bd%ae-locateallonscreen" &gt;尋找所有符合位置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%9c%a8%e5%9c%96%e7%89%87%e4%b8%ad%e6%90%9c%e5%b0%8b-locate%e9%9d%9e%e8%9e%a2%e5%b9%95" &gt;在圖片中搜尋&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%83%8f%e7%b4%a0%e9%a1%8f%e8%89%b2%e5%81%b5%e6%b8%ac" &gt;像素顏色偵測&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%8f%96%e5%be%97%e6%8c%87%e5%ae%9a%e5%ba%a7%e6%a8%99%e7%9a%84%e5%83%8f%e7%b4%a0%e9%a1%8f%e8%89%b2-pixel" &gt;取得指定座標的像素顏色&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#%e5%88%a4%e6%96%b7%e5%83%8f%e7%b4%a0%e9%a1%8f%e8%89%b2%e6%98%af%e5%90%a6%e7%ac%a6%e5%90%88-pixelmatchescolor" &gt;判斷像素顏色是否符合&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%af%a6%e6%88%b0%e7%af%84%e4%be%8b%e8%87%aa%e5%8b%95%e9%bb%9e%e6%93%8a%e6%8c%87%e5%ae%9a%e6%8c%89%e9%88%95" &gt;實戰範例：自動點擊指定按鈕&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e5%af%a6%e6%88%b0%e7%af%84%e4%be%8b%e7%ad%89%e5%be%85%e5%9c%96%e6%a1%88%e5%87%ba%e7%8f%be%e5%86%8d%e5%9f%b7%e8%a1%8c" &gt;實戰範例：等待圖案出現再執行&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%b8%b8%e8%a6%8b%e5%95%8f%e9%a1%8c%e8%88%87%e9%99%a4%e9%8c%af%e6%8a%80%e5%b7%a7" &gt;常見問題與除錯技巧&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-截取螢幕畫面"&gt;1. 截取螢幕畫面
&lt;/h3&gt;&lt;h4 id="全螢幕截圖-screenshot"&gt;全螢幕截圖 &lt;code&gt;screenshot()&lt;/code&gt;：
&lt;/h4&gt;&lt;p&gt;在 1920 x 1080 的螢幕上，&lt;code&gt;screenshot()&lt;/code&gt;大約需要 100 毫秒。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 截取全螢幕，回傳 PIL Image 物件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 顯示圖片（需安裝 Pillow）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img&lt;span style="color:#f92672"&gt;.&lt;/span&gt;show()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 直接儲存為檔案&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot(&lt;span style="color:#e6db74"&gt;&amp;#39;screenshot.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 截圖並儲存&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot(&lt;span style="color:#e6db74"&gt;&amp;#39;my_screen.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;截圖尺寸：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;img&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# (寬, 高)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="區域截圖-screenshotregion"&gt;區域截圖 &lt;code&gt;screenshot(region=(...)))&lt;/code&gt;：
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;region&lt;/code&gt; 參數格式為 &lt;code&gt;(left, top, width, height)&lt;/code&gt;，分別表示要截圖區域的左側、頂部、寬度和高度。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 截取左上角 400x300 的區域&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot(region&lt;span style="color:#f92672"&gt;=&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 截取螢幕中央的 200x200 區域&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;screen_w, screen_h &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cx &lt;span style="color:#f92672"&gt;=&lt;/span&gt; screen_w &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cy &lt;span style="color:#f92672"&gt;=&lt;/span&gt; screen_h &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot(region&lt;span style="color:#f92672"&gt;=&lt;/span&gt;(cx, cy, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img&lt;span style="color:#f92672"&gt;.&lt;/span&gt;save(&lt;span style="color:#e6db74"&gt;&amp;#39;center_crop.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 補充說明：PIL Image 物件&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;screenshot()&lt;/code&gt; 回傳的是 &lt;a class="link" href="https://pillow.readthedocs.io/" target="_blank" rel="noopener"
&gt;Pillow&lt;/a&gt; 的 &lt;code&gt;Image&lt;/code&gt; 物件，可以進一步進行旋轉、裁切、濾鏡等影像處理，也可以轉成 NumPy 陣列配合 OpenCV 使用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; numpy &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; np
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; cv2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;img &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt; np&lt;span style="color:#f92672"&gt;.&lt;/span&gt;array(img)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;frame &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cv2&lt;span style="color:#f92672"&gt;.&lt;/span&gt;cvtColor(frame, cv2&lt;span style="color:#f92672"&gt;.&lt;/span&gt;COLOR_RGB2BGR)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cv2&lt;span style="color:#f92672"&gt;.&lt;/span&gt;imwrite(&lt;span style="color:#e6db74"&gt;&amp;#39;screen_cv2.png&amp;#39;&lt;/span&gt;, frame)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="2-圖像辨識在螢幕上尋找圖片"&gt;2. 圖像辨識：在螢幕上尋找圖片
&lt;/h3&gt;&lt;p&gt;PyAutoGUI 可以在當前螢幕畫面中搜尋指定圖片，回傳其位置。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;前置條件&lt;/strong&gt;：需先準備好要搜尋的圖片（&lt;code&gt;.png&lt;/code&gt; 格式最佳），可用截圖工具截取目標按鈕或圖案。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 注意：新舊版本差異&lt;/strong&gt;&lt;br&gt;
在新版 PyAutoGUI 中，若找不到圖片會拋出 &lt;code&gt;pyautogui.ImageNotFoundException&lt;/code&gt;；如果是舊版，則會回傳 &lt;code&gt;None&lt;/code&gt;，可以使用 &lt;code&gt;if location:&lt;/code&gt; 來判斷。&lt;br&gt;
建議統一使用 &lt;code&gt;try...except&lt;/code&gt; 來確保程式不會崩潰。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id="尋找圖片位置-locateonscreen"&gt;尋找圖片位置 &lt;code&gt;locateOnScreen()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 在螢幕上尋找 button.png，找到則回傳 Box(left, top, width, height)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; location &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateOnScreen(&lt;span style="color:#e6db74"&gt;&amp;#39;button.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;找到圖片：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;location&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# location.left, location.top, location.width, location.height&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;找不到圖片&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="取得圖片中心座標-locatecenteronscreen"&gt;取得圖片中心座標 &lt;code&gt;locateCenterOnScreen()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 找到圖片並直接回傳中心點座標 (x, y)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; center &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateCenterOnScreen(&lt;span style="color:#e6db74"&gt;&amp;#39;button.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; x, y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; center
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;圖片中心：(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;x&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;y&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(x, y)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;找不到圖片&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="調整信心值confidence"&gt;調整信心值（confidence）
&lt;/h4&gt;&lt;p&gt;當截圖與螢幕畫面因縮放或顯示設定略有差異時，可調整 &lt;code&gt;confidence&lt;/code&gt; 參數（需安裝 &lt;code&gt;opencv-python&lt;/code&gt;）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip install opencv-python
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# confidence 介於 0~1，預設為 1.0（完全相符）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 降低至 0.8 可接受 80% 的相似度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; location &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateOnScreen(&lt;span style="color:#e6db74"&gt;&amp;#39;button.png&amp;#39;&lt;/span&gt;, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.8&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;找到圖片：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;location&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;找不到圖片&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="尋找所有符合位置-locateallonscreen"&gt;尋找所有符合位置 &lt;code&gt;locateAllOnScreen()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 回傳所有符合位置的 generator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; all_locations &lt;span style="color:#f92672"&gt;=&lt;/span&gt; list(pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateAllOnScreen(&lt;span style="color:#e6db74"&gt;&amp;#39;icon.png&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;共找到 &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;len(all_locations)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 個符合位置&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; loc &lt;span style="color:#f92672"&gt;in&lt;/span&gt; all_locations:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(loc)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;找不到任何符合的圖片&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="在圖片中搜尋-locate非螢幕"&gt;在圖片中搜尋 &lt;code&gt;locate()&lt;/code&gt;（非螢幕）：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; PIL &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Image
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 在一張截圖檔案中搜尋另一張圖片&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;haystack &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Image&lt;span style="color:#f92672"&gt;.&lt;/span&gt;open(&lt;span style="color:#e6db74"&gt;&amp;#39;fullscreen.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;needle &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Image&lt;span style="color:#f92672"&gt;.&lt;/span&gt;open(&lt;span style="color:#e6db74"&gt;&amp;#39;button.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; location &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locate(needle, haystack, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.9&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(location)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;在圖片中找不到目標圖案&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="3-像素顏色偵測"&gt;3. 像素顏色偵測
&lt;/h3&gt;&lt;h4 id="取得指定座標的像素顏色-pixel"&gt;取得指定座標的像素顏色 &lt;code&gt;pixel()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 取得座標 (100, 200) 的 RGB 顏色值&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;r, g, b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pixel(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;顏色：RGB(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;r&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;g&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;b&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="判斷像素顏色是否符合-pixelmatchescolor"&gt;判斷像素顏色是否符合 &lt;code&gt;pixelMatchesColor()&lt;/code&gt;：
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 判斷 (100, 200) 是否為紅色 (255, 0, 0)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;is_red &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pixelMatchesColor(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, (&lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;是紅色嗎？&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;is_red&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 允許一定的顏色誤差（tolerance 預設為 0）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;is_approx_red &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pixelMatchesColor(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, (&lt;span style="color:#ae81ff"&gt;255&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;), tolerance&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;接近紅色嗎？&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;is_approx_red&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;實用應用：偵測載入完成&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 等待某個座標的顏色變成綠色（表示載入完成）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;等待載入完成...&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r, g, b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pixel(&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; g &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; &lt;span style="color:#f92672"&gt;and&lt;/span&gt; r &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt; &lt;span style="color:#f92672"&gt;and&lt;/span&gt; b &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;: &lt;span style="color:#75715e"&gt;# 接近綠色&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;載入完成！&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="4-實戰範例自動點擊指定按鈕"&gt;4. 實戰範例：自動點擊指定按鈕
&lt;/h3&gt;&lt;p&gt;以下範例示範如何找到螢幕上的「送出」按鈕圖片並自動點擊：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;click_button&lt;/span&gt;(image_path, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.9&lt;/span&gt;, timeout&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; 在螢幕上搜尋圖片並點擊，若超過 timeout 秒仍找不到則拋出例外。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; start_time &lt;span style="color:#f92672"&gt;=&lt;/span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;time()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; location &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateCenterOnScreen(image_path, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;confidence)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; location:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(location)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;成功點擊：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;image_path&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;time() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; start_time &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; timeout:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TimeoutError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;超過 &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;timeout&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 秒仍找不到：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;image_path&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 使用範例&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; click_button(&lt;span style="color:#e6db74"&gt;&amp;#39;submit_button.png&amp;#39;&lt;/span&gt;, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.85&lt;/span&gt;, timeout&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;15&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TimeoutError&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;錯誤：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;e&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="5-實戰範例等待圖案出現再執行"&gt;5. 實戰範例：等待圖案出現再執行
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;wait_for_image&lt;/span&gt;(image_path, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.85&lt;/span&gt;, check_interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; 持續等待直到螢幕出現指定圖片，回傳中心座標。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;等待出現：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;image_path&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; center &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateCenterOnScreen(image_path, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;confidence)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; center:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;已找到，座標：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;center&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; center
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(check_interval)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 完整自動化流程範例&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;,&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;) &lt;span style="color:#75715e"&gt;# 等待目標程式啟動&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 等待「登入」按鈕出現&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;login_btn &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wait_for_image(&lt;span style="color:#e6db74"&gt;&amp;#39;login_button.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(login_btn)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 輸入帳號密碼&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;user@example.com&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.05&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;press(&lt;span style="color:#e6db74"&gt;&amp;#39;tab&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;typewrite(&lt;span style="color:#e6db74"&gt;&amp;#39;mypassword&amp;#39;&lt;/span&gt;, interval&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.05&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 等待「送出」按鈕出現並點擊&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;submit_btn &lt;span style="color:#f92672"&gt;=&lt;/span&gt; wait_for_image(&lt;span style="color:#e6db74"&gt;&amp;#39;submit_button.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(submit_btn)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 等待「歡迎」畫面出現，確認登入成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wait_for_image(&lt;span style="color:#e6db74"&gt;&amp;#39;welcome_screen.png&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;登入成功！&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="6-常見問題與除錯技巧"&gt;6. 常見問題與除錯技巧
&lt;/h3&gt;&lt;h4 id="問題-1locateonscreen-找不到圖片"&gt;問題 1：&lt;code&gt;locateOnScreen()&lt;/code&gt; 找不到圖片
&lt;/h4&gt;&lt;p&gt;可能原因與解決方式：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;解決方式&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;螢幕縮放不同（HiDPI / Retina）&lt;/td&gt;
&lt;td&gt;降低 &lt;code&gt;confidence&lt;/code&gt; 值，例如 &lt;code&gt;0.8&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;圖片有些微色差&lt;/td&gt;
&lt;td&gt;加入 &lt;code&gt;grayscale=True&lt;/code&gt; 進行灰階比對&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;圖片不在當前可視範圍&lt;/td&gt;
&lt;td&gt;確認視窗是否最小化或被遮擋&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;圖片解析度不符&lt;/td&gt;
&lt;td&gt;重新截取在目標機器上的按鈕圖片&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 灰階比對，對顏色差異更有容忍度&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;location &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateOnScreen(&lt;span style="color:#e6db74"&gt;&amp;#39;button.png&amp;#39;&lt;/span&gt;, grayscale&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.8&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="問題-2macos-截圖需要授權"&gt;問題 2：macOS 截圖需要授權
&lt;/h4&gt;&lt;p&gt;在 macOS 上，需至「系統設定 → 隱私權與安全性 → 螢幕錄製」，將執行 Python 的終端機加入允許清單。&lt;/p&gt;
&lt;h4 id="問題-3速度太快造成錯誤"&gt;問題 3：速度太快造成錯誤
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 增加全域延遲&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;PAUSE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0.3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 或在關鍵步驟加入 time.sleep()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;0.5&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="取得當前像素顏色的小工具"&gt;取得當前像素顏色的小工具
&lt;/h4&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;,&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;移動滑鼠到目標位置，3 秒後擷取顏色&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x, y &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;position()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;r, g, b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pixel(x, y)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;座標 (&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;x&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;y&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;) 的顏色：RGB(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;r&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;g&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;b&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：截取並儲存指定區域&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：截取螢幕右下角 300x200 的區域，儲存為 &lt;code&gt;corner.png&lt;/code&gt;。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;w, h &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;size()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;region &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (w &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, h &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;300&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;screenshot(&lt;span style="color:#e6db74"&gt;&amp;#39;corner.png&amp;#39;&lt;/span&gt;, region&lt;span style="color:#f92672"&gt;=&lt;/span&gt;region)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;已儲存右下角截圖：corner.png&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：確認按鈕存在才點擊&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：嘗試在螢幕上尋找 &lt;code&gt;ok_button.png&lt;/code&gt;，若找到則點擊，否則印出「找不到按鈕」。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; center &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;locateCenterOnScreen(&lt;span style="color:#e6db74"&gt;&amp;#39;ok_button.png&amp;#39;&lt;/span&gt;, confidence&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0.85&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; center:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;click(center)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;已點擊 OK 按鈕&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;except&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;ImageNotFoundException:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;找不到按鈕&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：像素顏色監控&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：每秒偵測座標 (960, 540) 的像素顏色，若 R 值超過 200 則印出警告訊息並停止，最多監控 30 秒。&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; pyautogui
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;開始監控像素顏色...&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r, g, b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; pyautogui&lt;span style="color:#f92672"&gt;.&lt;/span&gt;pixel(&lt;span style="color:#ae81ff"&gt;960&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;540&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;第 &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 秒：RGB(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;r&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;g&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;, &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;b&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;)&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; r &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;⚠️ 警告：R 值超過 200！&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; time&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sleep(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;監控結束，未觸發警告&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;hr&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://pyautogui.readthedocs.io/en/latest/screenshot.html" target="_blank" rel="noopener"
&gt;PyAutoGUI 官方文件 — Screenshot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pyautogui.readthedocs.io/en/latest/screenshot.html#locating-on-screen" target="_blank" rel="noopener"
&gt;PyAutoGUI 官方文件 — Locating&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://pillow.readthedocs.io/en/stable/" target="_blank" rel="noopener"
&gt;Pillow 官方文件&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html" target="_blank" rel="noopener"
&gt;OpenCV-Python 教學&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/asweigart/pyautogui" target="_blank" rel="noopener"
&gt;PyAutoGUI GitHub 原始碼&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://automatetheboringstuff.com/2e/chapter20/" target="_blank" rel="noopener"
&gt;Automate the Boring Stuff with Python — Chapter 20&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI 路徑參數</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-path-parameters/</link><pubDate>Mon, 27 Apr 2026 14:00:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-path-parameters/</guid><description>&lt;p&gt;在&lt;a class="link" href="https://Dandelionlibra.github.io/post/python/how-to-use-fastapi/" &gt;第一篇教學&lt;/a&gt;中，我們建立了一個簡單的 &lt;code&gt;/&lt;/code&gt; 根目錄端點。但真實世界的 API 通常需要根據不同的請求回傳不同的資料，例如：取得「特定 ID」的使用者資料。&lt;/p&gt;
&lt;p&gt;這時候，我們就需要用到&lt;strong&gt;路徑參數（Path Parameters）&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%ad%98%e4%bb%80%e9%ba%bc%e6%98%af%e5%8b%95%e6%85%8b-url" &gt;前置知識：什麼是「動態 URL」？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e4%bb%80%e9%ba%bc%e6%98%af%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8" &gt;什麼是路徑參數？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%ae%a3%e5%91%8a%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8" &gt;宣告路徑參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%8a%a0%e4%b8%8a%e5%9e%8b%e5%88%a5%e6%8f%90%e7%a4%ba%e8%87%aa%e5%8b%95%e5%9e%8b%e5%88%a5%e8%bd%89%e6%8f%9b" &gt;加上型別提示：自動型別轉換&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e8%87%aa%e5%8b%95%e8%b3%87%e6%96%99%e9%a9%97%e8%ad%89422-%e9%8c%af%e8%aa%a4" &gt;自動資料驗證（422 錯誤）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e8%b7%af%e5%be%91%e9%a0%86%e5%ba%8f%e7%9a%84%e9%87%8d%e8%a6%81%e6%80%a7" &gt;路徑順序的重要性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e4%bd%bf%e7%94%a8-enum-%e9%99%90%e5%88%b6%e5%90%88%e6%b3%95%e9%81%b8%e9%a0%85" &gt;使用 &lt;code&gt;Enum&lt;/code&gt; 限制合法選項&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#8-%e5%8c%85%e5%90%ab%e8%b7%af%e5%be%91%e5%88%86%e9%9a%94%e7%ac%a6%e7%9a%84%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8" &gt;包含路徑分隔符的路徑參數&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-前置知識什麼是動態-url"&gt;1. 前置知識：什麼是「動態 URL」？
&lt;/h3&gt;&lt;p&gt;靜態 URL 是固定的網址，例如 &lt;code&gt;/users/list&lt;/code&gt;，每次造訪都會看到一樣的東西。&lt;/p&gt;
&lt;p&gt;而&lt;strong&gt;動態 URL&lt;/strong&gt; 則允許網址的某個部分變成「變數」。例如購物網站的商品網址可能是 &lt;code&gt;/products/1&lt;/code&gt;、&lt;code&gt;/products/2&lt;/code&gt;，其中 &lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;2&lt;/code&gt; 就是動態改變的部分。後端程式會讀取這個數字，然後從資料庫撈出對應的商品資料。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-什麼是路徑參數"&gt;2. 什麼是路徑參數？
&lt;/h3&gt;&lt;p&gt;在 URL 中，那個會動態改變的部分，我們就稱為&lt;strong&gt;路徑參數&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在 FastAPI 裡，我們可以使用類似 Python &lt;code&gt;f-string&lt;/code&gt; 的語法，用大括號 &lt;code&gt;{}&lt;/code&gt; 將 URL 中的某個部分包起來，宣告它是一個變數。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="3-宣告路徑參數"&gt;3. 宣告路徑參數
&lt;/h3&gt;&lt;p&gt;讓我們在 &lt;code&gt;main.py&lt;/code&gt; 中新增一個端點，用來取得特定使用者的資料：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_user&lt;/span&gt;(user_id):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個例子中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/users/{user_id}&lt;/code&gt; 告訴 FastAPI：「&lt;code&gt;/users/&lt;/code&gt; 後面的那一截字串，請當作變數 &lt;code&gt;user_id&lt;/code&gt;」。&lt;/li&gt;
&lt;li&gt;路由函式 &lt;code&gt;def read_user(user_id):&lt;/code&gt; 必須接收同名的參數 &lt;code&gt;user_id&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你在瀏覽器輸入 &lt;code&gt;http://127.0.0.1:8000/users/alice&lt;/code&gt;，就會看到回傳：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ 注意：不要宣告重複的路徑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果你寫了兩個一模一樣的路由，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/test&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_1&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;Test&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/test&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test_2&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;Test&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;FastAPI 的比對規則是 &lt;strong&gt;「從上往下掃描，先搶先贏」&lt;/strong&gt;。當有人造訪 &lt;code&gt;/test&lt;/code&gt; 時，任務會立刻交給第一個 &lt;code&gt;test_1&lt;/code&gt; 處理並結束。
下面的 &lt;code&gt;test_2&lt;/code&gt; 函式會變成永遠等不到客人的「幽靈程式碼」。此外，這還會導致自動產生的 API 文件（Swagger UI）發生覆蓋錯亂。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="4-加上型別提示自動型別轉換"&gt;4. 加上型別提示：自動型別轉換
&lt;/h3&gt;&lt;p&gt;在上面的例子中，&lt;code&gt;user_id&lt;/code&gt; 預設會被當作&lt;strong&gt;字串（string）&lt;/strong&gt;。但如果使用者的 ID 在資料庫中是整數（Integer）怎麼辦？&lt;/p&gt;
&lt;p&gt;在 FastAPI 中，只需要使用 Python 標準的&lt;strong&gt;型別提示（Type Hints）&lt;/strong&gt;，FastAPI 就會自動幫忙做轉換。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_item&lt;/span&gt;(item_id: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: type(item_id)&lt;span style="color:#f92672"&gt;.&lt;/span&gt;__name__}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當你造訪 &lt;code&gt;http://127.0.0.1:8000/items/5&lt;/code&gt;，FastAPI 發現你宣告了 &lt;code&gt;item_id: int&lt;/code&gt;，就會自動把網址中的字串 &lt;code&gt;&amp;quot;5&amp;quot;&lt;/code&gt; 轉換成整數 &lt;code&gt;5&lt;/code&gt; 傳進函式中。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="5-自動資料驗證422-錯誤"&gt;5. 自動資料驗證（422 錯誤）
&lt;/h3&gt;&lt;p&gt;延續上面的例子，如果有人亂輸入網址，造訪了 &lt;code&gt;http://127.0.0.1:8000/items/foo&lt;/code&gt;，會發生什麼？字串 &lt;code&gt;&amp;quot;foo&amp;quot;&lt;/code&gt; 沒辦法變成整數啊。&lt;/p&gt;
&lt;p&gt;程式不會崩潰，FastAPI 會直接攔截這個錯誤，並回傳清楚的 HTTP 錯誤狀態碼 &lt;strong&gt;422 Unprocessable Entity&lt;/strong&gt;（無法處理的實體），並附帶錯誤說明：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;int_parsing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be a valid integer, unable to parse string as an integer&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;錯誤訊息明確指出：在路徑（path）中的 &lt;code&gt;item_id&lt;/code&gt; 發生了轉換整數失敗的錯誤。這省去了我們自己寫 &lt;code&gt;if not item_id.isdigit(): ...&lt;/code&gt; 等檢查邏輯。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="6-路徑順序的重要性"&gt;6. 路徑順序的重要性
&lt;/h3&gt;&lt;p&gt;在設計 API 時，有時候會同時有「固定路徑」和「動態路徑」。&lt;/p&gt;
&lt;p&gt;例如，我們有一個取得所有使用者的端點 &lt;code&gt;/users/all&lt;/code&gt;，也有一個取得特定使用者的端點 &lt;code&gt;/users/{user_id}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;在 FastAPI 中，&lt;strong&gt;路由的宣告順序非常重要&lt;/strong&gt;。程式碼是從上往下執行的，FastAPI 會比對第一個符合條件的路徑。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;錯誤的寫法：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_user&lt;/span&gt;(user_id: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/all&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_all_users&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果這樣寫，當你造訪 &lt;code&gt;/users/all&lt;/code&gt; 時，FastAPI 會先看到 &lt;code&gt;@app.get(&amp;quot;/users/{user_id}&amp;quot;)&lt;/code&gt;，然後把 &lt;code&gt;&amp;quot;all&amp;quot;&lt;/code&gt; 當作是變數 &lt;code&gt;user_id&lt;/code&gt; 的值傳進去，這是錯誤的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確的寫法（固定路徑在前）：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/all&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_all_users&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_user&lt;/span&gt;(user_id: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;把固定的路由寫在前面，就能確保它會被正確攔截。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="7-使用-enum-限制合法選項"&gt;7. 使用 &lt;code&gt;Enum&lt;/code&gt; 限制合法選項
&lt;/h3&gt;&lt;p&gt;有時候，路徑參數不能隨便亂填，只能是幾個特定的選項。這時候可以使用 Python 內建的 &lt;code&gt;Enum&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;假設我們有一個機器學習模型 API，只接受 &lt;code&gt;alexnet&lt;/code&gt;、&lt;code&gt;resnet&lt;/code&gt;、&lt;code&gt;lenet&lt;/code&gt; 三種模型名稱：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; enum &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Enum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ModelName&lt;/span&gt;(str, Enum):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alexnet &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;alexnet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resnet &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;resnet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lenet &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;lenet&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/models/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{model_name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_model&lt;/span&gt;(model_name: ModelName):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; model_name &lt;span style="color:#f92672"&gt;==&lt;/span&gt; ModelName&lt;span style="color:#f92672"&gt;.&lt;/span&gt;alexnet:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;: model_name, &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Deep Learning FTW!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; model_name&lt;span style="color:#f92672"&gt;.&lt;/span&gt;value &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;lenet&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;: model_name, &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;LeCNN all the masses&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;: model_name, &lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Have some residuals&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;這個寫法有三個好處：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;編輯器支援&lt;/strong&gt;：打字時會有自動補全（Auto-completion）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自動驗證&lt;/strong&gt;：如果使用者輸入 &lt;code&gt;http://127.0.0.1:8000/models/yolov4&lt;/code&gt;，FastAPI 會直接回傳 422 錯誤，並告訴使用者有哪些合法的選項。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;enum&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;model_name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be &amp;#39;alexnet&amp;#39;, &amp;#39;resnet&amp;#39; or &amp;#39;lenet&amp;#39;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;yolov4&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;ctx&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;expected&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;alexnet&amp;#39;, &amp;#39;resnet&amp;#39; or &amp;#39;lenet&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;API 文件&lt;/strong&gt;：Swagger UI 會自動把這個欄位變成一個&lt;strong&gt;下拉式選單&lt;/strong&gt;，讓測試更方便！&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="8-包含路徑分隔符的路徑參數"&gt;8. 包含路徑分隔符的路徑參數
&lt;/h3&gt;&lt;p&gt;最後一個比較特殊的情境是：如果路徑參數本身就是一個檔案路徑（包含 &lt;code&gt;/&lt;/code&gt;）怎麼辦？&lt;/p&gt;
&lt;p&gt;例如 URL 長這樣：&lt;code&gt;/files/home/johndoe/myfile.txt&lt;/code&gt;。
我們希望 &lt;code&gt;/files/&lt;/code&gt; 後面的所有東西 &lt;code&gt;home/johndoe/myfile.txt&lt;/code&gt; 都被當成一個變數。&lt;/p&gt;
&lt;p&gt;FastAPI 支援 Starlette 的特殊路徑語法：&lt;code&gt;:path&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/files/{file_path:path}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_file&lt;/span&gt;(file_path: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;file_path&amp;#34;&lt;/span&gt;: file_path}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只要加上 &lt;code&gt;:path&lt;/code&gt;，FastAPI 就不會因為遇到 &lt;code&gt;/&lt;/code&gt; 就把字串切斷，而是會把它一字不漏地全部抓下來。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試範例：&lt;/strong&gt;
如果在瀏覽器輸入 &lt;code&gt;http://127.0.0.1:8000/files/home/myfile.txt&lt;/code&gt;，將會收到完整的路徑字串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;file_path&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;home/myfile.txt&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：撰寫產品分類 API&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 API 端點 &lt;code&gt;GET /categories/{category_name}&lt;/code&gt;。
這個端點應該接收字串型別的 &lt;code&gt;category_name&lt;/code&gt;，並回傳 JSON 格式的分類名稱。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/categories/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{category_name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_category&lt;/span&gt;(category_name: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;category&amp;#34;&lt;/span&gt;: category_name}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：修復順序錯誤&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：以下的程式碼有個問題。如果造訪 &lt;code&gt;/posts/latest&lt;/code&gt; 會發生什麼事？請將程式碼修改成正確的順序。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{post_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_post&lt;/span&gt;(post_id: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post_id&amp;#34;&lt;/span&gt;: post_id}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/latest&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_latest_post&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;This is the latest post!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;p&gt;如果照著原本的寫法，造訪 &lt;code&gt;/posts/latest&lt;/code&gt; 時，FastAPI 會以為 &lt;code&gt;&amp;quot;latest&amp;quot;&lt;/code&gt; 是 &lt;code&gt;post_id&lt;/code&gt;。由於 &lt;code&gt;post_id&lt;/code&gt; 規定要是整數（&lt;code&gt;int&lt;/code&gt;），所以會丟出 &lt;strong&gt;422 Validation Error&lt;/strong&gt; 錯誤。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確寫法&lt;/strong&gt;（把固定路徑移到上面）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/latest&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_latest_post&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;This is the latest post!&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/posts/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{post_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_post&lt;/span&gt;(post_id: int):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;post_id&amp;#34;&lt;/span&gt;: post_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：使用 Enum 限制顏色&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：建立一個 &lt;code&gt;GET /items/color/{color_name}&lt;/code&gt; 的端點。
請使用 &lt;code&gt;Enum&lt;/code&gt; 限制 &lt;code&gt;color_name&lt;/code&gt; 只能是 &lt;code&gt;red&lt;/code&gt;、&lt;code&gt;green&lt;/code&gt; 或 &lt;code&gt;blue&lt;/code&gt; 其中一種。如果輸入合法，回傳對應的顏色名稱。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; enum &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Enum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ColorName&lt;/span&gt;(str, Enum):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; red &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;red&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; green &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;green&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; blue &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;blue&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/color/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{color_name}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_color&lt;/span&gt;(color_name: ColorName):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;selected_color&amp;#34;&lt;/span&gt;: color_name}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/path-params/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 路徑參數&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI 查詢參數</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-query-parameters/</link><pubDate>Mon, 27 Apr 2026 18:55:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-query-parameters/</guid><description>&lt;p&gt;在上一篇，我們學習了如何使用「路徑參數」來建立動態的 URL，例如透過 &lt;code&gt;/users/alice&lt;/code&gt; 來取得特定使用者的資料。
然而，如果我們想要在一個網頁中加入**「搜尋關鍵字」&lt;strong&gt;、&lt;/strong&gt;「分頁（第幾頁）」&lt;strong&gt;或是&lt;/strong&gt;「篩選條件（價格由高到低）」**，把這些條件全部塞進斜線 &lt;code&gt;/&lt;/code&gt; 裡面會變得非常難以管理。&lt;/p&gt;
&lt;p&gt;這時候，需要用到另一種常見的傳值方式：&lt;strong&gt;查詢參數（Query Parameters）&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e4%bb%80%e9%ba%bc%e6%98%af%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;什麼是查詢參數？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e5%ae%a3%e5%91%8a%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;宣告查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e8%a8%ad%e5%ae%9a%e9%a0%90%e8%a8%ad%e5%80%bc%e9%81%b8%e5%a1%ab%e5%8f%83%e6%95%b8" &gt;設定預設值（選填參數）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%bf%85%e5%a1%ab%e7%9a%84%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;必填的查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e4%bd%bf%e7%94%a8-optional-%e8%a1%a8%e7%a4%ba%e5%8f%af%e9%81%b8%e6%ac%84%e4%bd%8d" &gt;使用 Optional 表示可選欄位&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%b8%83%e6%9e%97%e5%80%bc-bool-%e7%9a%84%e8%87%aa%e5%8b%95%e8%bd%89%e6%8f%9b" &gt;布林值 (bool) 的自動轉換&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e5%90%8c%e6%99%82%e4%bd%bf%e7%94%a8%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8%e8%88%87%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;同時使用路徑參數與查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-什麼是查詢參數"&gt;1. 什麼是查詢參數？
&lt;/h3&gt;&lt;p&gt;大家一定有用過 Google 搜尋。當你在 Google 搜尋「FastAPI」時，你會發現網址列變成這樣：
&lt;code&gt;https://www.google.com/search?q=FastAPI&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在這裡，&lt;code&gt;?&lt;/code&gt; 後面的字串就是&lt;strong&gt;查詢參數&lt;/strong&gt;。它的語法規則非常簡單：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;以問號 &lt;code&gt;?&lt;/code&gt; 作為開頭。&lt;/li&gt;
&lt;li&gt;格式是 &lt;code&gt;鍵=值&lt;/code&gt;（Key=Value），例如 &lt;code&gt;q=FastAPI&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;如果有多個參數，就用 &lt;code&gt;&amp;amp;&lt;/code&gt; 符號串接起來，例如 &lt;code&gt;?q=FastAPI&amp;amp;page=2&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="2-宣告查詢參數"&gt;2. 宣告查詢參數
&lt;/h3&gt;&lt;p&gt;在 FastAPI 中，要宣告查詢參數非常簡單。&lt;strong&gt;只要在路由函式中加入的參數名稱，沒有出現在路徑 &lt;code&gt;@app.get(...)&lt;/code&gt; 的 &lt;code&gt;{}&lt;/code&gt; 裡面，FastAPI 就會自動把它當作查詢參數！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以下是簡單的分頁 API 範例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_items&lt;/span&gt;(skip: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, limit: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: skip, &lt;span style="color:#e6db74"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: limit}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個例子中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路徑是 &lt;code&gt;/items/&lt;/code&gt;，裡面&lt;strong&gt;沒有&lt;/strong&gt;任何 &lt;code&gt;{}&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;函式 &lt;code&gt;read_items&lt;/code&gt; 卻接收了 &lt;code&gt;skip&lt;/code&gt; 和 &lt;code&gt;limit&lt;/code&gt; 兩個參數。&lt;/li&gt;
&lt;li&gt;因此，FastAPI 會自動認定 &lt;code&gt;skip&lt;/code&gt; 和 &lt;code&gt;limit&lt;/code&gt; 是從網址 &lt;code&gt;?&lt;/code&gt; 後面傳進來的查詢參數。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="3-設定預設值選填參數"&gt;3. 設定預設值（選填參數）
&lt;/h3&gt;&lt;p&gt;上面的範例中，我們給了參數預設值：&lt;code&gt;skip: int = 0&lt;/code&gt; 和 &lt;code&gt;limit: int = 10&lt;/code&gt;。
這代表這兩個參數是&lt;strong&gt;選填的（Optional）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果你直接造訪 &lt;code&gt;http://127.0.0.1:8000/items/&lt;/code&gt;：
由於你沒有在網址提供參數，FastAPI 會使用你設定的預設值，回傳：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果你造訪 &lt;code&gt;http://127.0.0.1:8000/items/?skip=20&lt;/code&gt;：
FastAPI 會讀取網址的 &lt;code&gt;skip&lt;/code&gt;，而 &lt;code&gt;limit&lt;/code&gt; 依然使用預設值：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果你造訪 &lt;code&gt;http://127.0.0.1:8000/items/?skip=20&amp;amp;limit=5&lt;/code&gt;：
FastAPI 會同時讀取網址的 &lt;code&gt;skip&lt;/code&gt; 和 &lt;code&gt;limit&lt;/code&gt;：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;skip&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;#34;limit&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="4-必填的查詢參數"&gt;4. 必填的查詢參數
&lt;/h3&gt;&lt;p&gt;如果我們希望某個查詢參數是**必填（Required）**的，該怎麼做？
很簡單，&lt;strong&gt;不要給它預設值就好了！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;假設我們正在寫一個搜尋 API，使用者一定要提供搜尋的關鍵字 &lt;code&gt;q&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_items&lt;/span&gt;(q: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;search_keyword&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因為 &lt;code&gt;q&lt;/code&gt; 沒有等號 &lt;code&gt;=&lt;/code&gt; 賦予預設值，它就變成了必填參數。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;
如果你沒有提供 &lt;code&gt;q&lt;/code&gt;，直接造訪 &lt;code&gt;http://127.0.0.1:8000/search&lt;/code&gt;，FastAPI 會非常貼心地自動擋下來，並回傳 422 錯誤，告訴客戶端少傳了資料：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;missing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;query&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Field required&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;必須輸入 &lt;code&gt;http://127.0.0.1:8000/search?q=apple&lt;/code&gt; 才能成功取得結果。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="5-使用-optional-表示可選欄位"&gt;5. 使用 Optional 表示可選欄位
&lt;/h3&gt;&lt;p&gt;有時候，我們希望一個參數是選填的，但在使用者沒有提供時，我們希望它的值是 &lt;code&gt;None&lt;/code&gt;（空值），而不是某個具體的數字或字串。&lt;/p&gt;
&lt;p&gt;這時候建議搭配 Python &lt;code&gt;typing&lt;/code&gt; 模組中的 &lt;code&gt;Optional&lt;/code&gt; 來寫，這可以讓編輯器的提示更準確，程式碼也更有可讀性：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/products&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_products&lt;/span&gt;(category: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; category:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;顯示 &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;category&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt; 類別的產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;顯示所有產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的 &lt;code&gt;Optional[str] = None&lt;/code&gt; 告訴了任何人（包含編輯器與 FastAPI）：「這個參數是字串，但它是可選的，如果不填，預設就是 None」。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果造訪 &lt;code&gt;http://127.0.0.1:8000/products&lt;/code&gt;（不提供參數）：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;顯示所有產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;如果造訪 &lt;code&gt;http://127.0.0.1:8000/products?category=3C&lt;/code&gt;（提供參數）：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;顯示 3C 類別的產品&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="6-布林值-bool-的自動轉換"&gt;6. 布林值 (bool) 的自動轉換
&lt;/h3&gt;&lt;p&gt;FastAPI 的資料轉換機制非常聰明，尤其是對於布林值（Boolean）。&lt;/p&gt;
&lt;p&gt;如果宣告了一個布林型別的查詢參數：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/active&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_active_users&lt;/span&gt;(short_format: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; short_format:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [{&lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;}, {&lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;}]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當你要在網址中把 &lt;code&gt;short_format&lt;/code&gt; 設為 &lt;code&gt;True&lt;/code&gt; 時，你不用只打 &lt;code&gt;true&lt;/code&gt;。FastAPI 會自動識別以下所有字眼，並把它們轉換成 Python 的 &lt;code&gt;True&lt;/code&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;?short_format=true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=True&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=on&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;?short_format=yes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;相反地，&lt;code&gt;false&lt;/code&gt;、&lt;code&gt;0&lt;/code&gt;、&lt;code&gt;off&lt;/code&gt;、&lt;code&gt;no&lt;/code&gt; 都會被轉換為 Python 的 &lt;code&gt;False&lt;/code&gt;。這對前端工程師來說是一個極度友善的設計！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;造訪 &lt;code&gt;http://127.0.0.1:8000/users/active?short_format=1&lt;/code&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;造訪 &lt;code&gt;http://127.0.0.1:8000/users/active?short_format=0&lt;/code&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;#34;users&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;,&lt;span style="color:#f92672"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;25&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bob&amp;#34;&lt;/span&gt;,&lt;span style="color:#f92672"&gt;&amp;#34;age&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="7-同時使用路徑參數與查詢參數"&gt;7. 同時使用路徑參數與查詢參數
&lt;/h3&gt;&lt;p&gt;在實務上，最常把「路徑參數」和「查詢參數」混在一起使用。
例如：若想查看「特定使用者（路徑）」底下的「某些物品，並進行分頁（查詢）」。&lt;/p&gt;
&lt;p&gt;不用擔心順序問題，FastAPI 會根據變數名稱自動幫你分類：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/items&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_user_items&lt;/span&gt;(user_id: int, short: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;False&lt;/span&gt;, skip: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; item_data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id, &lt;span style="color:#e6db74"&gt;&amp;#34;items&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;筆電&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;滑鼠&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; short:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id, &lt;span style="color:#e6db74"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;ok&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; item_data
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個例子中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_id&lt;/code&gt; 因為出現在 &lt;code&gt;@app.get&lt;/code&gt; 的 &lt;code&gt;{}&lt;/code&gt; 中，所以它是&lt;strong&gt;路徑參數&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;short&lt;/code&gt; 和 &lt;code&gt;skip&lt;/code&gt; 沒有出現在路徑中，所以它們是&lt;strong&gt;查詢參數&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果造訪：&lt;code&gt;http://127.0.0.1:8000/users/5/items?short=yes&amp;amp;skip=1&lt;/code&gt;
FastAPI 就能將 &lt;code&gt;user_id&lt;/code&gt; 解析為 &lt;code&gt;5&lt;/code&gt;，&lt;code&gt;short&lt;/code&gt; 解析為 &lt;code&gt;True&lt;/code&gt;，&lt;code&gt;skip&lt;/code&gt; 解析為 &lt;code&gt;1&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：建立排序 API&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 API 端點 &lt;code&gt;GET /books&lt;/code&gt;。
該端點需要接收一個查詢參數 &lt;code&gt;sort_by&lt;/code&gt;。如果使用者沒有提供，預設應該是 &lt;code&gt;&amp;quot;id&amp;quot;&lt;/code&gt;。如果使用者提供，就回傳使用者輸入的排序方式。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/books&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_books&lt;/span&gt;(sort_by: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;sort_by&amp;#34;&lt;/span&gt;: sort_by}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：必填與選填混搭&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 API 端點 &lt;code&gt;GET /search/videos&lt;/code&gt;。
該端點需要一個&lt;strong&gt;必填&lt;/strong&gt;的查詢參數 &lt;code&gt;keyword&lt;/code&gt;（字串），以及一個&lt;strong&gt;選填&lt;/strong&gt;的參數 &lt;code&gt;max_results&lt;/code&gt;（整數，預設為 50）。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search/videos&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_videos&lt;/span&gt;(keyword: str, max_results: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;keyword&amp;#34;&lt;/span&gt;: keyword, &lt;span style="color:#e6db74"&gt;&amp;#34;max_results&amp;#34;&lt;/span&gt;: max_results}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：如何判斷參數類型？&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：觀察以下的程式碼，請問 &lt;code&gt;model_id&lt;/code&gt; 和 &lt;code&gt;version&lt;/code&gt; 分別是什麼類型的參數？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/models/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{model_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;check_model&lt;/span&gt;(model_id: int, version: float):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;model_id&lt;/code&gt; 是&lt;strong&gt;路徑參數&lt;/strong&gt;，因為它被宣告在 &lt;code&gt;@app.get&lt;/code&gt; 的路徑字串 &lt;code&gt;{}&lt;/code&gt; 中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;version&lt;/code&gt; 是&lt;strong&gt;查詢參數&lt;/strong&gt;，因為它沒有出現在路徑字串中，而且它是必填的（沒有預設值）。造訪時的網址會像這樣：&lt;code&gt;/models/10?version=1.5&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/query-params/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI Request Body</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-request-body/</link><pubDate>Mon, 27 Apr 2026 23:21:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-request-body/</guid><description>&lt;p&gt;在前幾篇文章中，我們學會了如何透過「網址（URL）」把資料傳給伺服器，例如：路徑參數 &lt;code&gt;/users/123&lt;/code&gt; 或是查詢參數 &lt;code&gt;?skip=10&lt;/code&gt;。
但如果要傳送&lt;strong&gt;大量資料&lt;/strong&gt;（例如：一整篇部落格文章的內容）或是&lt;strong&gt;機密資料&lt;/strong&gt;（例如：登入的帳號密碼），把它們全部塞進網址裡就不太適合了。這時候需要使用 &lt;strong&gt;Request Body（請求本文）&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%ad%98http-%e6%96%b9%e6%b3%95%e8%88%87-pydantic" &gt;前置知識：HTTP 方法與 Pydantic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e4%bb%80%e9%ba%bc%e6%98%af-request-body" &gt;什麼是 Request Body？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e5%bb%ba%e7%ab%8b%e7%ac%ac%e4%b8%80%e5%80%8b-pydantic-%e6%a8%a1%e5%9e%8b" &gt;建立第一個 Pydantic 模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-%e5%9c%a8%e8%b7%af%e7%94%b1%e5%87%bd%e5%bc%8f%e4%b8%ad%e5%ae%a3%e5%91%8a-body-%e5%8f%83%e6%95%b8" &gt;在路由函式中宣告 Body 參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e9%81%b8%e5%a1%ab%e6%ac%84%e4%bd%8d%e8%88%87%e9%a0%90%e8%a8%ad%e5%80%bc%e8%a8%ad%e5%ae%9a" &gt;選填欄位與預設值設定&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-%e5%9c%a8%e5%87%bd%e5%bc%8f%e5%85%a7%e9%83%a8%e4%bd%bf%e7%94%a8%e6%a8%a1%e5%9e%8b%e8%b3%87%e6%96%99" &gt;在函式內部使用模型資料&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e4%b8%89%e5%90%88%e4%b8%80%e6%a8%a1%e5%9e%8b--%e8%b7%af%e5%be%91%e5%8f%83%e6%95%b8--%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8" &gt;三合一：模型 + 路徑參數 + 查詢參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#8-pydantic-%e7%9a%84%e8%87%aa%e5%8b%95%e9%a9%97%e8%ad%89%e6%a9%9f%e5%88%b6" &gt;Pydantic 的自動驗證機制&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-前置知識http-方法與-pydantic"&gt;1. 前置知識：HTTP 方法與 Pydantic
&lt;/h3&gt;&lt;p&gt;在開始實作之前，需要先了解三個重要觀念：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;為什麼要用 POST？&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;GET&lt;/code&gt; 請求是用來「取得」資料，傳統上不會夾帶 Body。如果要傳送資料給伺服器來「新增」或「修改」東西，通常會使用 &lt;code&gt;POST&lt;/code&gt;、&lt;code&gt;PUT&lt;/code&gt; 或 &lt;code&gt;PATCH&lt;/code&gt; 這些 HTTP 方法。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;什麼是 Pydantic？&lt;/strong&gt;&lt;br&gt;
FastAPI 的強大，有一半歸功於 &lt;strong&gt;Pydantic&lt;/strong&gt;。Pydantic 是一個 Python 的資料驗證庫，它的核心概念是：&lt;strong&gt;如果資料驗證通過了，拿到的一定是預期的型別&lt;/strong&gt;；如果驗證失敗，它會直接拋出清楚的錯誤，不會讓錯誤的資料跑進系統。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;什麼是 &lt;code&gt;class&lt;/code&gt; 繼承？&lt;/strong&gt;&lt;br&gt;
在 Python 裡，&lt;code&gt;class&lt;/code&gt;（類別）用來定義一個物件的藍圖。當我們寫 &lt;code&gt;class Item(BaseModel):&lt;/code&gt; 時，代表 &lt;code&gt;Item&lt;/code&gt; 這個模型「繼承」了 Pydantic 提供的 &lt;code&gt;BaseModel&lt;/code&gt;，這讓 &lt;code&gt;Item&lt;/code&gt; 瞬間擁有了自動轉換 JSON、檢查型別的超能力！&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="2-什麼是-request-body"&gt;2. 什麼是 Request Body？
&lt;/h3&gt;&lt;p&gt;Request Body（請求本文）是指客戶端（如瀏覽器、手機 App）在發送 HTTP 請求時，夾帶在請求底層的資料，最常見的格式就是 &lt;strong&gt;JSON&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;例如，當想在購物車裡新增一個商品時，客戶端會送出這樣一段 JSON：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;滑鼠&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;999.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;is_offer&amp;#34;&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="3-建立第一個-pydantic-模型"&gt;3. 建立第一個 Pydantic 模型
&lt;/h3&gt;&lt;p&gt;在 FastAPI 中，要接收並處理上面的 JSON 資料，第一步就是利用 Pydantic 的 &lt;code&gt;BaseModel&lt;/code&gt; 來宣告資料結構。&lt;/p&gt;
&lt;p&gt;我們可以在檔案的最上方，把需要的工具匯入進來，然後定義一個 &lt;code&gt;Item&lt;/code&gt; 模型：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; is_offer: bool &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的寫法與標準的 Python 變數宣告一模一樣：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; 必須是字串 (&lt;code&gt;str&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;price&lt;/code&gt; 必須是浮點數 (&lt;code&gt;float&lt;/code&gt;)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;is_offer&lt;/code&gt; 是一個布林值 (&lt;code&gt;bool&lt;/code&gt;)，並且有一個預設值 &lt;code&gt;None&lt;/code&gt;（代表如果前端沒傳這個欄位也沒關係）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="4-在路由函式中宣告-body-參數"&gt;4. 在路由函式中宣告 Body 參數
&lt;/h3&gt;&lt;p&gt;有了模型之後，只要把它當作參數，加進路由函式裡就可以了！&lt;/p&gt;
&lt;p&gt;請注意，這次要用的是 &lt;code&gt;@app.post&lt;/code&gt;，因為是要「新增」資料：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_item&lt;/span&gt;(item: Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; item
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當 FastAPI 看到 &lt;code&gt;item: Item&lt;/code&gt; 時：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;它知道 &lt;code&gt;Item&lt;/code&gt; 是一個繼承自 &lt;code&gt;BaseModel&lt;/code&gt; 的類別。&lt;/li&gt;
&lt;li&gt;根據「路徑 vs 查詢」的規則，&lt;code&gt;item&lt;/code&gt; 既不在路徑 &lt;code&gt;{}&lt;/code&gt; 裡，也不是簡單的型別（如 &lt;code&gt;int&lt;/code&gt; 或 &lt;code&gt;str&lt;/code&gt;），所以 FastAPI 會自動把它判定為 &lt;strong&gt;Request Body&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;測試看看：&lt;/strong&gt;
啟動伺服器後，到 Swagger UI (&lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;) 找到 &lt;code&gt;POST /items/&lt;/code&gt;，點擊「Try it out」，你會發現 FastAPI 已經幫你準備好了一個可以填寫的 JSON 框，輸入資料後送出，就會原封不動地回傳你填入的資料。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="5-選填欄位與預設值設定"&gt;5. 選填欄位與預設值設定
&lt;/h3&gt;&lt;p&gt;和查詢參數一樣，可以使用 Python 的 &lt;code&gt;Optional&lt;/code&gt; 來讓模型的欄位意圖更清晰：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tax: Optional[float] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個模型中，&lt;code&gt;name&lt;/code&gt; 和 &lt;code&gt;price&lt;/code&gt; 是&lt;strong&gt;必填的&lt;/strong&gt;。而 &lt;code&gt;description&lt;/code&gt; 和 &lt;code&gt;tax&lt;/code&gt; 是&lt;strong&gt;選填的&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="6-在函式內部使用模型資料"&gt;6. 在函式內部使用模型資料
&lt;/h3&gt;&lt;p&gt;當 FastAPI 把 JSON 自動轉換為 Pydantic 模型（例如上方的 &lt;code&gt;item&lt;/code&gt; 變數）後，在函式內部就可以直接用「點 (&lt;code&gt;.&lt;/code&gt;)」來存取屬性，而且還有編輯器的自動補全功能！&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_item&lt;/span&gt;(item: Item):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 可以直接用 item.price，不需要像字典一樣寫成 item[&amp;#34;price&amp;#34;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total_price &lt;span style="color:#f92672"&gt;=&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;price
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tax:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; total_price &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;tax
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_name&amp;#34;&lt;/span&gt;: item&lt;span style="color:#f92672"&gt;.&lt;/span&gt;name, &lt;span style="color:#e6db74"&gt;&amp;#34;total_price&amp;#34;&lt;/span&gt;: total_price}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這在開發時非常舒服，因為再也不用擔心把欄位名稱打錯，編輯器都會幫忙檢查。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="7-三合一模型--路徑參數--查詢參數"&gt;7. 三合一：模型 + 路徑參數 + 查詢參數
&lt;/h3&gt;&lt;p&gt;FastAPI 最厲害的地方在於，可以毫無負擔地把三種參數全部混在一起用，而且依然能清晰地分類。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.put&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_item&lt;/span&gt;(item_id: int, item: Item, q: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; q:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result&lt;span style="color:#f92672"&gt;.&lt;/span&gt;update({&lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在這個端點中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;item_id&lt;/code&gt;：有出現在路徑 &lt;code&gt;/items/{item_id}&lt;/code&gt; 裡，所以是&lt;strong&gt;路徑參數&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt;：沒有出現在路徑裡，且型別是簡單的字串，所以是&lt;strong&gt;查詢參數&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;item&lt;/code&gt;：型別是 Pydantic 的 &lt;code&gt;Item&lt;/code&gt; 模型，所以是 &lt;strong&gt;Request Body&lt;/strong&gt;。
ㄋ&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="8-pydantic-的自動驗證機制"&gt;8. Pydantic 的自動驗證機制
&lt;/h3&gt;&lt;p&gt;如果客戶端傳來了錯誤型別的資料怎麼辦？例如，&lt;code&gt;price&lt;/code&gt; 應該要是數字，前端卻傳了一個字串：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;滑鼠&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;三百塊&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不用擔心程式崩潰，因為有 Pydantic 把關，FastAPI 會直接攔截下來，並回傳 422 錯誤告訴使用者：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;float_parsing&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be a valid number, unable to parse string as a number&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;三百塊&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意看這裡的 &lt;code&gt;loc&lt;/code&gt;，它會明確指出錯誤發生在 &lt;code&gt;&amp;quot;body&amp;quot;&lt;/code&gt;（Request Body）裡的 &lt;code&gt;&amp;quot;price&amp;quot;&lt;/code&gt; 欄位，這大大節省除錯的時間。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：建立使用者模型&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：請利用 Pydantic 建立一個名為 &lt;code&gt;User&lt;/code&gt; 的模型。
這個模型需要有以下三個欄位：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;username&lt;/code&gt;：字串（必填）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;email&lt;/code&gt;：字串（必填）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;age&lt;/code&gt;：整數（選填，預設為 18）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; email: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; age: Optional[int] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：宣告 POST 路由&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：接續上一題，請撰寫一個 API 端點 &lt;code&gt;POST /users/&lt;/code&gt;，並使用剛才建立的 &lt;code&gt;User&lt;/code&gt; 模型作為 Request Body。
在函式內部，請回傳一個包含 &lt;code&gt;message&lt;/code&gt; 和使用者名稱的 JSON，例如：&lt;code&gt;{&amp;quot;message&amp;quot;: &amp;quot;成功建立使用者：Alice&amp;quot;}&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.post&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;create_user&lt;/span&gt;(user: User):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;成功建立使用者：&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;username&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 3：參數分類大哉問&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：觀察以下的程式碼，請問 &lt;code&gt;user_id&lt;/code&gt;、&lt;code&gt;token&lt;/code&gt; 和 &lt;code&gt;profile&lt;/code&gt; 分別屬於哪一種參數（路徑、查詢、還是 Body）？&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.patch&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/profile/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_profile&lt;/span&gt;(user_id: int, profile: User, token: str):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;pass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_id&lt;/code&gt; 是&lt;strong&gt;路徑參數&lt;/strong&gt;（因為出現在路徑的 &lt;code&gt;{}&lt;/code&gt; 中）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;profile&lt;/code&gt; 是 &lt;strong&gt;Request Body&lt;/strong&gt;（因為型別為 Pydantic 模型 &lt;code&gt;User&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;token&lt;/code&gt; 是&lt;strong&gt;查詢參數&lt;/strong&gt;（沒有在路徑中，且是基本字串型別）。網址會長得像 &lt;code&gt;/profile/5?token=abc&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 請求本文&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI Body 進階：多個模型、巢狀結構與欄位驗證</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-body-advanced/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-body-advanced/</guid><description>&lt;p&gt;上一篇介紹了如何使用 Pydantic 建立基本的 Request Body 模型。然而，真實世界的 API 往往會面臨更複雜的需求：
如果想在同一個請求中同時接收「商品資料」和「使用者資料」該怎麼辦？如果模型裡面還包含著另一個模型（巢狀結構）呢？&lt;/p&gt;
&lt;p&gt;這篇將解鎖 FastAPI 與 Pydantic 的進階用法，讓 API 驗證變得更靈活。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%90%8c%e6%99%82%e6%8e%a5%e6%94%b6%e5%a4%9a%e5%80%8b-body-%e5%8f%83%e6%95%b8" &gt;同時接收多個 Body 參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e4%bd%bf%e7%94%a8-body-%e6%98%8e%e7%a2%ba%e5%ae%a3%e5%91%8a%e5%96%ae%e4%b8%80%e5%80%bc" &gt;使用 Body() 明確宣告單一值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-%e4%bd%bf%e7%94%a8-field-%e7%82%ba%e6%ac%84%e4%bd%8d%e5%8a%a0%e4%b8%8a%e9%a9%97%e8%ad%89" &gt;使用 Field() 為欄位加上驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-list-%e8%88%87-set%e8%99%95%e7%90%86%e9%99%a3%e5%88%97%e8%b3%87%e6%96%99" &gt;List 與 Set：處理陣列資料&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e5%b7%a2%e7%8b%80%e6%a8%a1%e5%9e%8b%e6%a8%a1%e5%9e%8b%e4%b8%ad%e7%9a%84%e6%a8%a1%e5%9e%8b" &gt;巢狀模型：模型中的模型&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-同時接收多個-body-參數"&gt;1. 同時接收多個 Body 參數
&lt;/h3&gt;&lt;p&gt;如果想在同一個路由中接收兩種不同的資料結構，例如：建立一筆訂單時，同時需要 &lt;code&gt;Item&lt;/code&gt; 商品資訊與 &lt;code&gt;User&lt;/code&gt; 買家資訊，直接把它們都宣告為參數即可，FastAPI 會自動將它們視為 Request Body。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;User&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; username: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; full_name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.put&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/orders/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{order_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_order&lt;/span&gt;(order_id: int, item: Item, user: User):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;order_id&amp;#34;&lt;/span&gt;: order_id, &lt;span style="color:#e6db74"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: user}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;當 FastAPI 看到兩個繼承自 &lt;code&gt;BaseModel&lt;/code&gt; 的參數時，它會預期客戶端傳來的 JSON 長這樣：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;機械鍵盤&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;username&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;alice123&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;full_name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Alice Wang&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;它會自動使用參數名稱（&lt;code&gt;item&lt;/code&gt; 和 &lt;code&gt;user&lt;/code&gt;）作為 JSON 最外層的 Key，並把資料完美解析出來。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-使用-body-明確宣告單一值"&gt;2. 使用 Body() 明確宣告單一值
&lt;/h3&gt;&lt;p&gt;有時候，除了完整的 Pydantic 模型之外，可能還想額外接收一個簡單的單一值，例如一個整數 &lt;code&gt;importance&lt;/code&gt;。
如果直接寫 &lt;code&gt;importance: int&lt;/code&gt;，FastAPI 會把它當作&lt;strong&gt;查詢參數&lt;/strong&gt;，因為它是基本型別。&lt;/p&gt;
&lt;p&gt;如果堅持要把這個簡單的值也放進 JSON Body 裡面，可以使用 FastAPI 提供的 &lt;code&gt;Body()&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Body, FastAPI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.put&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{item_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;update_item&lt;/span&gt;(item_id: int, item: Item, importance: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Body(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;)):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: item_id, &lt;span style="color:#e6db74"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: item, &lt;span style="color:#e6db74"&gt;&amp;#34;importance&amp;#34;&lt;/span&gt;: importance}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Body(...)&lt;/code&gt; 中的 &lt;code&gt;...&lt;/code&gt; 在 FastAPI 裡代表&lt;strong&gt;必填&lt;/strong&gt;的意思。&lt;/li&gt;
&lt;li&gt;此時，FastAPI 預期的 JSON 結構會變成：
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;item&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;機械鍵盤&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3000.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;importance&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="3-使用-field-為欄位加上驗證"&gt;3. 使用 Field() 為欄位加上驗證
&lt;/h3&gt;&lt;p&gt;如果想要對 Pydantic 模型裡面的某個欄位做更嚴格的限制（例如：價格必須大於 0、名字長度最多 50 字元），需要使用 Pydantic 的 &lt;code&gt;Field()&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, Field
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, max_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;商品的名稱&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description: str &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;商品詳細描述&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, gt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;價格必須大於 0&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的參數解釋如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;：代表必填。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_length=50&lt;/code&gt;：字串長度不能超過 50。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default=None&lt;/code&gt;：代表預設值為 &lt;code&gt;None&lt;/code&gt;（可選填）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gt=0&lt;/code&gt;：Greater Than，數值必須大於 0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;description&lt;/code&gt; / &lt;code&gt;title&lt;/code&gt;：這兩個參數不會影響驗證，但會顯示在 Swagger UI 文件上，幫助前端工程師理解欄位意義。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="4-list-與-set處理陣列資料"&gt;4. List 與 Set：處理陣列資料
&lt;/h3&gt;&lt;p&gt;如果某個欄位會包含多個值（例如：商品的標籤清單），在 Python 中可以使用 &lt;code&gt;list&lt;/code&gt; 或 &lt;code&gt;set&lt;/code&gt; 型別。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tags: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; unique_tags: set[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;list[str]&lt;/code&gt; 代表這個欄位必須是一個列表，且裡面的元素都要是字串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;set[str]&lt;/code&gt; 與 list 類似，但如果是 &lt;code&gt;set&lt;/code&gt;，當前端傳來包含重複值的 JSON（例如 &lt;code&gt;[&amp;quot;電子&amp;quot;, &amp;quot;電子&amp;quot;, &amp;quot;促銷&amp;quot;]&lt;/code&gt;）時，FastAPI 轉成 Python &lt;code&gt;set&lt;/code&gt; 後會自動去重複，變成 &lt;code&gt;{&amp;quot;電子&amp;quot;, &amp;quot;促銷&amp;quot;}&lt;/code&gt;！&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="5-巢狀模型模型中的模型"&gt;5. 巢狀模型：模型中的模型
&lt;/h3&gt;&lt;p&gt;Pydantic 最強大的地方在於它的高階組合能力，可以把一個模型當作另一個模型中的欄位型別，創造出「巢狀結構」。&lt;/p&gt;
&lt;p&gt;例如，一個商品可能包含多個「圖片」：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, HttpUrl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Image&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; url: HttpUrl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; price: float
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 這個欄位是一個列表，且裡面的元素必須符合 Image 類&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; images: list[Image] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HttpUrl&lt;/code&gt; 是 Pydantic 內建的特殊型別，它會自動驗證傳入的字串是否為合法的網址。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此時，FastAPI 預期的 JSON 就會是一個深層的巢狀結構：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;遊戲主機&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;15000.0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;images&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;https://example.com/console_front.png&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;正面照&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;https://example.com/console_back.png&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;背面照&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：建立包含清單的模型&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：建立一個 &lt;code&gt;Post&lt;/code&gt;（部落格文章）模型，包含以下欄位：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt;：字串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;content&lt;/code&gt;：字串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;comments&lt;/code&gt;：字串的列表（預設為空列表）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Post&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comments: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：使用 Field() 加上限制&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：請修改上一題的 &lt;code&gt;Post&lt;/code&gt; 模型，要求 &lt;code&gt;title&lt;/code&gt; 的長度不可超過 100 個字元，並加上描述 &lt;code&gt;&amp;quot;文章的標題&amp;quot;&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, Field
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Post&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, max_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;文章的標題&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; content: str
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comments: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body-multiple-params/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - Body 多個參數&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body-fields/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 欄位&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/body-nested-models/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 巢狀模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>FastAPI 參數驗證：Query() 與 Path() 的完整用法</title><link>https://Dandelionlibra.github.io/2026/04/fastapi-parameter-validation/</link><pubDate>Tue, 28 Apr 2026 10:46:00 +0800</pubDate><guid>https://Dandelionlibra.github.io/2026/04/fastapi-parameter-validation/</guid><description>&lt;p&gt;在第 2 篇和第 3 篇中，我們學會了宣告&lt;code&gt;路徑參數&lt;/code&gt;和&lt;code&gt;查詢參數&lt;/code&gt;，FastAPI 已經幫我們做好了最基礎的「型別轉換」與「必填/選填」檢查。
但如果需要更嚴格的限制呢？例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;密碼長度至少要 8 個字元。&lt;/li&gt;
&lt;li&gt;商品 ID 必須大於 0。&lt;/li&gt;
&lt;li&gt;查詢參數的名稱在網址上是 &lt;code&gt;item-query&lt;/code&gt;（帶有橫線，在 Python 中是不合法的變數名稱）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;這時候，就需要 FastAPI 內建的 &lt;code&gt;Query()&lt;/code&gt; 與 &lt;code&gt;Path()&lt;/code&gt; 兩大工具了。&lt;/p&gt;
&lt;h2 id="內容大綱"&gt;內容大綱
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;a class="link" href="#1-%e5%89%8d%e7%bd%ae%e7%9f%a5%e8%ad%98%e6%ad%a3%e8%a6%8f%e8%a1%a8%e9%81%94%e5%bc%8f-regex" &gt;前置知識：正規表達式 (Regex)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#2-%e7%82%ba%e4%bb%80%e9%ba%bc%e9%9c%80%e8%a6%81-query-%e5%92%8c-path" &gt;為什麼需要 Query() 和 Path()？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#3-query-%e7%9a%84%e5%ad%97%e4%b8%b2%e8%88%87%e9%95%b7%e5%ba%a6%e9%a9%97%e8%ad%89" &gt;Query() 的字串與長度驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#4-query-%e8%a7%a3%e6%b1%ba%e5%8f%83%e6%95%b8%e5%88%a5%e5%90%8d%e8%88%87%e6%96%87%e4%bb%b6%e8%aa%aa%e6%98%8e" &gt;Query() 解決參數別名與文件說明&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#5-%e4%bd%bf%e7%94%a8-query-%e6%8e%a5%e6%94%b6%e5%a4%9a%e5%80%8b%e7%9b%b8%e5%90%8c%e5%90%8d%e7%a8%b1%e7%9a%84%e5%80%bc" &gt;使用 Query() 接收多個相同名稱的值&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#6-path-%e7%9a%84%e6%95%b8%e5%80%bc%e7%af%84%e5%9c%8d%e9%a9%97%e8%ad%89" &gt;Path() 的數值範圍驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="#7-%e5%b0%87%e5%a4%9a%e5%80%8b%e6%9f%a5%e8%a9%a2%e5%8f%83%e6%95%b8%e6%95%b4%e5%90%88%e6%88%90%e6%a8%a1%e5%9e%8b" &gt;將多個查詢參數整合成模型&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id="1-前置知識正規表達式-regex"&gt;1. 前置知識：正規表達式 (Regex)
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;正規表達式（Regular Expression，簡稱 Regex 或 RegExp）&lt;/strong&gt; 是一種用來「匹配字串模式」的語法。&lt;br&gt;
例如，如果要檢查一個字串是否完全由大寫和小寫的英文字母組成，可以使用正規表達式 &lt;code&gt;^[a-zA-Z]+$&lt;/code&gt;。&lt;br&gt;
FastAPI 允許我們直接把這段模式丟給它，它會自動幫我們檢查前端傳來的字串是否符合規則。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="2-為什麼需要-query-和-path"&gt;2. 為什麼需要 Query() 和 Path()？
&lt;/h3&gt;&lt;p&gt;之前在處理 Request Body 時，我們學過可以用 Pydantic 的 &lt;code&gt;Field()&lt;/code&gt; 來加上驗證，但&lt;strong&gt;查詢參數和路徑參數並不是 Pydantic 模型&lt;/strong&gt;，它們只是函式的參數。&lt;br&gt;
因此，FastAPI 提供了 &lt;code&gt;Query()&lt;/code&gt; 和 &lt;code&gt;Path()&lt;/code&gt;，讓你能為這些網址上的參數做到和 &lt;code&gt;Field()&lt;/code&gt; 一模一樣的驗證與文件宣告效果！&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="3-query-的字串與長度驗證"&gt;3. Query() 的字串與長度驗證
&lt;/h3&gt;&lt;p&gt;假設有一個搜尋 API，希望使用者傳入的關鍵字 &lt;code&gt;q&lt;/code&gt; 長度介於 3 到 50 個字元之間，並且只能包含英文字母：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI, Query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_items&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; q: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, min_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, max_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;, pattern&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;^[a-zA-Z]+$&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;results&amp;#34;&lt;/span&gt;: [{&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;}, {&lt;span style="color:#e6db74"&gt;&amp;#34;item_id&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Bar&amp;#34;&lt;/span&gt;}], &lt;span style="color:#e6db74"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Query(None, ...)&lt;/code&gt; 的第一個參數 &lt;code&gt;None&lt;/code&gt; 代表這是選填的預設值，如果這是一個必填參數，可以寫成 &lt;code&gt;Query(...)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pattern&lt;/code&gt; 會在幕後使用 Python 的 &lt;code&gt;re&lt;/code&gt; 模組進行正規表達式比對，如果使用者傳了包含數字的 &lt;code&gt;?q=hello123&lt;/code&gt;，FastAPI 會直接回傳 422 錯誤。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="4-query-解決參數別名與文件說明"&gt;4. Query() 解決參數別名與文件說明
&lt;/h3&gt;&lt;p&gt;有些時候，前端習慣用帶有橫線的參數名稱（如 &lt;code&gt;item-query&lt;/code&gt;），但在 Python 中，變數名稱不能有橫線（&lt;code&gt;-&lt;/code&gt; 會被當作減號）。
這時可以使用 &lt;code&gt;alias&lt;/code&gt;（別名）來解決這個衝突。&lt;/p&gt;
&lt;p&gt;此外，也可以加上 &lt;code&gt;title&lt;/code&gt;、&lt;code&gt;description&lt;/code&gt; 甚至標記為 &lt;code&gt;deprecated&lt;/code&gt;（已棄用）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/search&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;search_items&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; q: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alias&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;item-query&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;搜尋關鍵字&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;輸入想尋找的商品名稱，必須是英文字母。&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; deprecated&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;results&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;Foo&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Bar&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;在瀏覽器中，網址必須輸入 &lt;code&gt;http://127.0.0.1:8000/search/?item-query=foo&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在 Python 程式碼中，我們依然使用合法的變數名稱 &lt;code&gt;q&lt;/code&gt; 來接收它。&lt;/li&gt;
&lt;li&gt;在 Swagger UI 中，這個欄位會顯示詳細的中文說明，並且被畫上一條刪除線，提示開發者這個參數未來可能會被移除（&lt;code&gt;deprecated=True&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="5-使用-query-接收多個相同名稱的值"&gt;5. 使用 Query() 接收多個相同名稱的值
&lt;/h3&gt;&lt;p&gt;如果前端想要一次搜尋多個關鍵字，網址可能會長這樣：
&lt;code&gt;http://127.0.0.1:8000/items/?q=foo&amp;amp;q=bar&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;為了讓 FastAPI 把這兩個 &lt;code&gt;q&lt;/code&gt; 收集成一個 Python 列表（List），必須明確使用 &lt;code&gt;Query()&lt;/code&gt; 來宣告：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/items/multi&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_items_multi&lt;/span&gt;(q: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(default&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#34;apple&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;banana&amp;#34;&lt;/span&gt;])):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;q_list&amp;#34;&lt;/span&gt;: q}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果不加上 &lt;code&gt;Query()&lt;/code&gt;（例如唯獨宣告 &lt;code&gt;q: str = None&lt;/code&gt;），當網址包含多個相同參數時，FastAPI 只會抓到&lt;strong&gt;最後一個值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;錯誤測試範例（只宣告 q: str）&lt;/strong&gt;：
發送請求：&lt;code&gt;GET http://127.0.0.1:8000/items/multi?q=foo&amp;amp;q=bar&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;q&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：如果在參數寫了 &lt;code&gt;q: list[str]&lt;/code&gt; 但卻&lt;strong&gt;沒有&lt;/strong&gt;加上 &lt;code&gt;= Query()&lt;/code&gt;，FastAPI 會把 &lt;code&gt;q&lt;/code&gt; 當作是要從 JSON Request Body 接收的資料，並拋出錯誤。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;加上了 &lt;code&gt;Query()&lt;/code&gt; 後，回傳的才會是正確且完整的陣列：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正確測試範例（宣告 q: list[str] = Query(&amp;hellip;)）&lt;/strong&gt;：
發送請求：&lt;code&gt;GET http://127.0.0.1:8000/items/multi?q=foo&amp;amp;q=bar&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;q_list&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="6-path-的數值範圍驗證"&gt;6. Path() 的數值範圍驗證
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;Path()&lt;/code&gt; 的用法與 &lt;code&gt;Query()&lt;/code&gt; 幾乎完全一樣，唯一的差別是：&lt;strong&gt;它專門用在路徑參數上&lt;/strong&gt;。
由於路徑參數通常是 ID（數字），最常使用它來做數值範圍的限制。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/users/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{user_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_user&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_id: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Path(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, title&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;使用者 ID&amp;#34;&lt;/span&gt;, ge&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, le&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;: user_id}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;數值驗證參數非常直觀：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ge&lt;/code&gt;：Greater than or Equal（大於等於 &lt;code&gt;≥&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gt&lt;/code&gt;：Greater Than（大於 &lt;code&gt;&amp;gt;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;le&lt;/code&gt;：Less than or Equal（小於等於 &lt;code&gt;≤&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lt&lt;/code&gt;：Less Than（小於 &lt;code&gt;&amp;lt;&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在這個例子中，如果輸入 &lt;code&gt;/users/0&lt;/code&gt; 或 &lt;code&gt;/users/1001&lt;/code&gt;，都會被 FastAPI 的驗證機制阻擋下來。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;錯誤測試範例&lt;/strong&gt;：
發送請求：&lt;code&gt;GET http://127.0.0.1:8000/users/-14&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;detail&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;greater_than_equal&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;loc&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;path&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;user_id&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Input should be greater than or equal to 1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;-14&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;ctx&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;ge&amp;#34;&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="7-將多個查詢參數整合成模型"&gt;7. 將多個查詢參數整合成模型
&lt;/h3&gt;&lt;p&gt;如果一個 API 有高達十幾個查詢參數（例如搜尋頁面有分類、價格上下限、排序方式、頁碼&amp;hellip;），路由函式的參數列表會變得長得嚇人。&lt;/p&gt;
&lt;p&gt;在較新的 FastAPI 版本中，可以把這些「查詢參數」也打包成一個 Pydantic 模型，只要在依賴注入時加上 &lt;code&gt;Depends()&lt;/code&gt; 即可。（關於 &lt;code&gt;Depends()&lt;/code&gt; 我們會在後面的章節詳細介紹，現在先記住這個好用的寫法）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Depends
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; pydantic &lt;span style="color:#f92672"&gt;import&lt;/span&gt; BaseModel, Field
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;FilterParams&lt;/span&gt;(BaseModel):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; limit: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;, gt&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, le&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; offset: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Field(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, ge&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; order_by: str &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;created_at&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/products/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read_products&lt;/span&gt;(filters: FilterParams &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Depends()):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;filters&amp;#34;&lt;/span&gt;: filters}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;現在，只要客戶端造訪 &lt;code&gt;/products/?limit=50&amp;amp;offset=10&amp;amp;order_by=price&lt;/code&gt;，FastAPI 就會自動幫你把這些散落的查詢參數，組裝成一個漂亮的 &lt;code&gt;FilterParams&lt;/code&gt; 物件。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="練習題"&gt;練習題
&lt;/h2&gt;&lt;details&gt;
&lt;summary&gt;📝 練習題 1：混合驗證&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 &lt;code&gt;GET /books/{book_id}&lt;/code&gt; 的端點。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;路徑參數 &lt;code&gt;book_id&lt;/code&gt; 必須是整數，且大於等於 1。&lt;/li&gt;
&lt;li&gt;查詢參數 &lt;code&gt;author&lt;/code&gt; 是選填的字串，長度至少 2 個字元。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; typing &lt;span style="color:#f92672"&gt;import&lt;/span&gt; Optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI, Path, Query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/books/&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{book_id}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_book&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; book_id: int &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Path(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;, ge&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author: Optional[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(&lt;span style="color:#66d9ef"&gt;None&lt;/span&gt;, min_length&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;book_id&amp;#34;&lt;/span&gt;: book_id, &lt;span style="color:#e6db74"&gt;&amp;#34;author&amp;#34;&lt;/span&gt;: author}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/details&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;📝 練習題 2：陣列查詢參數&lt;/summary&gt;
&lt;p&gt;&lt;strong&gt;題目&lt;/strong&gt;：撰寫一個 &lt;code&gt;GET /tags/&lt;/code&gt; 端點。
它可以接收多個同名的查詢參數 &lt;code&gt;tag&lt;/code&gt;（例如：&lt;code&gt;?tag=python&amp;amp;tag=web&lt;/code&gt;）。請確保 &lt;code&gt;tag&lt;/code&gt; 是必填的陣列。&lt;/p&gt;
&lt;hr&gt;
&lt;details&gt;
&lt;summary&gt;答案：&lt;/summary&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; fastapi &lt;span style="color:#f92672"&gt;import&lt;/span&gt; FastAPI, Query
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FastAPI()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.get&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;/tags/&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;get_tags&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tag: list[str] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; Query(&lt;span style="color:#f92672"&gt;...&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;: tag}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的 &lt;code&gt;Query(...)&lt;/code&gt; 中的 &lt;code&gt;...&lt;/code&gt; 代表這是必填的。如果不提供任何 &lt;code&gt;?tag=&lt;/code&gt;，將會拋出 422 錯誤。&lt;/p&gt;
&lt;/details&gt;
&lt;/details&gt;
&lt;h2 id="參考資料"&gt;參考資料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/query-params-str-validations/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 查詢參數與字串驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/path-params-numeric-validations/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 路徑參數與數值驗證&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://fastapi.tiangolo.com/zh-hant/tutorial/query-param-models/" target="_blank" rel="noopener"
&gt;FastAPI 官方教學 - 查詢參數模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>