[{"data":1,"prerenderedAt":3418},["ShallowReactive",2],{"blog-strands-agents-tool-executor":3,"related-strands-agents-tool-executor":211},{"id":4,"title":5,"author":6,"body":7,"category":193,"date":194,"description":195,"draft":196,"extension":197,"image":198,"meta":199,"navigation":200,"path":201,"seo":202,"stem":203,"tags":204,"__hash__":210},"blog/blog/strands-agents-tool-executor.md","Strands Agents Tool Executor Behavior Research","Ting Zhang",{"type":8,"value":9,"toc":184},"minimark",[10,14,18,99,111,119,133,137,143,147,153,156,162,170,174],[11,12,13],"h2",{"id":13},"前言",[15,16,17],"p",{},"寫 Chatbot POC 的時候有個功能需求是提供”串並行 Call 小腦 Agent “的功能\n因為使用的 Multi agent framework 是 Strands Agents，這樣就有很多方法可以實現了。",[19,20,21,44],"table",{},[22,23,24],"thead",{},[25,26,27,34,39],"tr",{},[28,29,30],"th",{},[31,32,33],"strong",{},"方法",[28,35,36],{},[31,37,38],{},"Concept",[28,40,41],{},[31,42,43],{},"Concern",[45,46,47,71,85],"tbody",{},[25,48,49,59,62],{},[50,51,52],"td",{},[53,54,58],"a",{"href":55,"rel":56},"https://strandsagents.com/latest/documentation/docs/user-guide/concepts/multi-agent/graph/",[57],"nofollow","Graph Multi-Agent Pattern",[50,60,61],{},"類似 LangGraph",[50,63,64,65,68,70],{},"如何“幫agent決定他要用哪個Tools?”",[66,67],"br",{},[66,69],{},"Conditional Edge 如何實現？Performance如何？",[25,72,73,80,83],{},[50,74,75],{},[53,76,79],{"href":77,"rel":78},"https://strandsagents.com/latest/documentation/docs/user-guide/concepts/multi-agent/swarm/",[57],"Swarm Multi-Agent Pattern",[50,81,82],{},"不負責任的 Graph Pattern，想怎麼連 Edge 全靠 LLM 決定",[50,84],{},[25,86,87,94,97],{},[50,88,89],{},[53,90,93],{"href":91,"rel":92},"https://strandsagents.com/latest/documentation/docs/user-guide/concepts/multi-agent/agents-as-tools/",[57],"Agents as tools",[50,95,96],{},"解釋如下圖",[50,98],{},[100,101,102],"ul",{},[103,104,105,106],"li",{},"Agent as tool\nStrands Agents 號稱可以透過Prompts來自動處理 Tools 的呼叫。\n",[107,108],"img",{"alt":109,"src":110},"agent loop","/images/blog/strands-agents-tool-executor/agent-loop.png",[15,112,113,114,118],{},"對於 ",[115,116,117],"code",{},"Tool Execution"," 有分成",[100,120,121,124],{},[103,122,123],{},"Concurrent executor (Default)",[103,125,126,127,132],{},"Sequential executor\n其實",[53,128,131],{"href":129,"rel":130,"title":129},"https://strandsagents.com/latest/documentation/docs/user-guide/concepts/tools/executors/?h=tool+executor",[57],"官方文檔","也沒有說得很清楚Concurrent / Sequential 到底差在哪？ex: 是否會繼承上下文？什麼情況應該用哪個的指引，所以直接上實驗結果：",[11,134,136],{"id":135},"測試-1-concurrent-executor","測試 1: Concurrent Executor",[15,138,139],{},[107,140],{"alt":141,"src":142},"concurrent result","/images/blog/strands-agents-tool-executor/concurrent-result.png",[11,144,146],{"id":145},"測試-2-sequential-executor","測試 2: Sequential Executor",[15,148,149],{},[107,150],{"alt":151,"src":152},"sequential result","/images/blog/strands-agents-tool-executor/sequential-result.png",[11,154,155],{"id":155},"結論",[157,158,159],"blockquote",{},[15,160,161],{},"沒有經過 Agent loop Reasoning 的話，就不會重新整理上下文提供給 agent tool，是獨立事件，跟你的executor選哪個沒關係．",[15,163,164,165,169],{},"Lab 實作程式放在 ",[53,166,167],{"href":167,"rel":168},"https://github.com/GoingCloud/Alpha-Lab/tree/main/strands-agents-lab/lab-1",[57],"Connect your Github account",[11,171,173],{"id":172},"refs","Refs",[15,175,176,181],{},[53,177,180],{"href":178,"rel":179},"https://mscript.atlassian.net/wiki/spaces/4a1b8ae8e58a442d95aae1898bcbff8f/blog/2025/11/10/11141550",[57],"Strands agents",[53,182,129],{"href":129,"rel":183},[57],{"title":185,"searchDepth":186,"depth":186,"links":187},"",2,[188,189,190,191,192],{"id":13,"depth":186,"text":13},{"id":135,"depth":186,"text":136},{"id":145,"depth":186,"text":146},{"id":155,"depth":186,"text":155},{"id":172,"depth":186,"text":173},"AI","2026-05-10","Exploring the design principles, behavioral patterns, and implementation details of the Tool Executor in the Strands Agents architecture, with a comparison of performance between different concurrency and asynchronous techniques in this context.",false,"md","/images/blog/strands-agents-tool-executor/banner.jpeg",{},true,"/blog/strands-agents-tool-executor",{"title":5,"description":195},"blog/strands-agents-tool-executor",[205,206,207,208,209],"LLM","Agents","Tool Executor","Python","Architecture","Ib2N-eaMM_HpWvew358gAIQiCH4sHvVpF7IcWz2ZyvQ",[212,1687,2082],{"id":213,"title":214,"author":6,"body":215,"category":1676,"date":1677,"description":1678,"draft":196,"extension":197,"image":185,"meta":1679,"navigation":200,"path":1680,"seo":1681,"stem":1682,"tags":1683,"__hash__":1686},"blog/blog/concurrency.md","Python 並發處理方法",{"type":8,"value":216,"toc":1655},[217,221,224,227,230,235,282,286,321,325,358,362,380,383,525,528,1226,1229,1481,1485,1491,1509,1513,1518,1536,1539,1544,1548,1551,1555,1558,1572,1576,1579,1625,1628,1651],[11,218,220],{"id":219},"這篇文章的用處","這篇文章的用處？",[15,222,223],{},"若專案內有遇到效能瓶頸，這可能可以幫助你加速。",[15,225,226],{},"Python 提供了幾種並發方式，每種方式適用於不同類型的任務。",[11,228,229],{"id":229},"各方式介紹",[231,232,234],"h3",{"id":233},"_1-多線程multithreading","1. 多線程（Multithreading）",[100,236,237,243,264,270,276],{},[103,238,239,242],{},[31,240,241],{},"特點","：由於 GIL 的存在，多線程無法實現真正的並行，線程間由解釋器切換執行。",[103,244,245,248,249,252,253],{},[31,246,247],{},"適用場景","：適合 ",[31,250,251],{},"I/O 綁定任務","，如：\n",[100,254,255,258,261],{},[103,256,257],{},"API 呼叫",[103,259,260],{},"檔案讀寫",[103,262,263],{},"使用者輸入等待",[103,265,266,269],{},[31,267,268],{},"限制","：在 CPU 綁定任務（如計算 Fibonacci 數列）中，效能與單線程迴圈相差無幾。",[103,271,272,275],{},[31,273,274],{},"優點","：線程間資料共享簡單，記憶體開銷低。",[103,277,278,281],{},[31,279,280],{},"缺點","：受 GIL 限制，無法利用多核心。",[231,283,285],{"id":284},"_2-多進程multiprocessing","2. 多進程（Multiprocessing）",[100,287,288,293,311,316],{},[103,289,290,292],{},[31,291,241],{},"：每個進程擁有獨立的 Python 解釋器和 GIL，因此可以在不同 CPU 核心上實現真正的並行。",[103,294,295,248,297,252,300],{},[31,296,247],{},[31,298,299],{},"CPU 綁定任務",[100,301,302,305,308],{},[103,303,304],{},"數學運算",[103,306,307],{},"圖像處理",[103,309,310],{},"日誌分析",[103,312,313,315],{},[31,314,274],{},"：繞過 GIL，實現真並行，速度較快。",[103,317,318,320],{},[31,319,280],{},"：記憶體使用量高，進程間資料共享較複雜（需使用管道或共享記憶體）。",[231,322,324],{"id":323},"_3-asyncio","3. Asyncio",[100,326,327,332,348,353],{},[103,328,329,331],{},[31,330,241],{},"：使用協程（coroutines）實現非阻塞的並發，適合 I/O 綁定任務。",[103,333,334,336,337],{},[31,335,247],{},"：\n",[100,338,339,342,345],{},[103,340,341],{},"網路請求",[103,343,344],{},"檔案 I/O",[103,346,347],{},"其他等待外部資源的任務",[103,349,350,352],{},[31,351,274],{},"：輕量，記憶體使用效率高。",[103,354,355,357],{},[31,356,280],{},"：不適合 CPU 密集型任務，因為它並非真正的並行。",[231,359,361],{"id":360},"_4-第三方庫","4. 第三方庫",[100,363,364,370,375],{},[103,365,366,369],{},[31,367,368],{},"例子","：Cython、NumPy、Pandas、PyTorch 等。",[103,371,372,374],{},[31,373,241],{},"：這些庫通常使用 C 語言實現，部分操作可繞過 GIL，提供高效能。",[103,376,377,379],{},[31,378,247],{},"：特定計算密集型任務（如矩陣運算）。",[11,381,382],{"id":382},"特性比較表",[19,384,385,404],{},[22,386,387],{},[25,388,389,392,395,398,401],{},[28,390,391],{},"特性",[28,393,394],{},"Multi-threading",[28,396,397],{},"Multi-processing",[28,399,400],{},"AsyncIO",[28,402,403],{},"Disabled GIL",[45,405,406,423,440,458,475,491,506],{},[25,407,408,413,416,419,421],{},[50,409,410],{},[31,411,412],{},"並行",[50,414,415],{},"❌",[50,417,418],{},"✅",[50,420,415],{},[50,422,418],{},[25,424,425,430,433,436,438],{},[50,426,427],{},[31,428,429],{},"記憶體共享",[50,431,432],{},"容易",[50,434,435],{},"困難",[50,437,432],{},[50,439,432],{},[25,441,442,447,450,453,455],{},[50,443,444],{},[31,445,446],{},"記憶體使用",[50,448,449],{},"低",[50,451,452],{},"高",[50,454,449],{},[50,456,457],{},"中等",[25,459,460,465,468,471,473],{},[50,461,462],{},[31,463,464],{},"CPU 密集型",[50,466,467],{},"差",[50,469,470],{},"好",[50,472,467],{},[50,474,470],{},[25,476,477,482,484,486,489],{},[50,478,479],{},[31,480,481],{},"I/O 密集型",[50,483,470],{},[50,485,457],{},[50,487,488],{},"最佳",[50,490,470],{},[25,492,493,498,500,502,504],{},[50,494,495],{},[31,496,497],{},"實作複雜度",[50,499,457],{},[50,501,452],{},[50,503,457],{},[50,505,452],{},[25,507,508,513,516,519,522],{},[50,509,510],{},[31,511,512],{},"競爭條件風險",[50,514,515],{},"低（GIL保護）",[50,517,518],{},"低（隔離）",[50,520,521],{},"無（單執行緒）",[50,523,524],{},"高（需手動處理）",[11,526,527],{"id":527},"範例程式碼",[529,530,534],"pre",{"className":531,"code":532,"language":533,"meta":185,"style":185},"language-python shiki shiki-themes github-light github-dark","import time\nimport threading\nimport multiprocessing\nimport asyncio\nimport sys\nfrom concurrent.futures import ThreadPoolExecutor\nfrom functools import wraps\n\n# 計時裝飾器，用於測量執行時間\ndef timing_decorator(func):\n    @wraps(func)\n    def wrapper(*args, **kwargs):\n        start = time.time()\n        result = func(*args, **kwargs)\n        end = time.time()\n        print(f\"{func.__name__} 執行時間: {end - start:.4f} 秒\")\n        return result\n    return wrapper\n\n# CPU 密集型任務：計算 Fibonacci 數列\ndef fib(n):\n    if n \u003C= 1:\n        return n\n    return fib(n - 1) + fib(n - 2)\n\n# I/O 綁定任務：模擬網路請求（全局函數）\ndef sync_io_task():\n    time.sleep(1)  # 模擬 1 秒的 I/O 延遲\n\n# 異步 I/O 任務\nasync def io_bound_task():\n    await asyncio.sleep(1)  # 模擬 1 秒的 I/O 延遲\n    return \"I/O 任務完成\"\n\n# 多線程實現\n@timing_decorator\ndef run_multithreading(n_tasks, task_type=\"cpu\"):\n    threads = []\n    if task_type == \"cpu\":\n        for _ in range(n_tasks):\n            t = threading.Thread(target=fib, args=(35,))\n            threads.append(t)\n            t.start()\n        for t in threads:\n            t.join()\n    else:  # I/O 任務\n        for _ in range(n_tasks):\n            t = threading.Thread(target=sync_io_task)\n            threads.append(t)\n            t.start()\n        for t in threads:\n            t.join()\n\n# 多進程實現\n@timing_decorator\ndef run_multiprocessing(n_tasks, task_type=\"cpu\"):\n    processes = []\n    if task_type == \"cpu\":\n        for _ in range(n_tasks):\n            p = multiprocessing.Process(target=fib, args=(35,))\n            processes.append(p)\n            p.start()\n        for p in processes:\n            p.join()\n    else:  # I/O 任務\n        for _ in range(n_tasks):\n            p = multiprocessing.Process(target=sync_io_task)\n            processes.append(p)\n            p.start()\n        for p in processes:\n            p.join()\n\n# Asyncio 實現\n@timing_decorator\nasync def run_asyncio(n_tasks):\n    tasks = [io_bound_task() for _ in range(n_tasks)]\n    await asyncio.gather(*tasks)\n\n# 禁用 GIL 的多線程實現（需 Python 3.13 --disable-gil）\n@timing_decorator\ndef run_nogil_multithreading(n_tasks, task_type=\"cpu\"):\n    with ThreadPoolExecutor(max_workers=n_tasks) as executor:\n        if task_type == \"cpu\":\n            futures = [executor.submit(fib, 35) for _ in range(n_tasks)]\n        else:  # I/O 任務\n            futures = [executor.submit(sync_io_task) for _ in range(n_tasks)]\n        for future in futures:\n            future.result()\n\n# 主程式\nif __name__ == \"__main__\":\n    n_tasks = 4  # 任務數量\n\n    print(\"=== CPU 密集型任務 (計算 Fibonacci 數列) ===\")\n    print(\"多線程 (Multithreading):\")\n    run_multithreading(n_tasks, task_type=\"cpu\")\n\n    print(\"\\n多進程 (Multiprocessing):\")\n    run_multiprocessing(n_tasks, task_type=\"cpu\")\n\n    if sys.version_info >= (3, 13) and hasattr(sys, \"disable_gil\"):\n        print(\"\\n禁用 GIL 的多線程:\")\n        run_nogil_multithreading(n_tasks, task_type=\"cpu\")\n    else:\n        print(\"\\n禁用 GIL 的多線程: 需要 Python 3.13 並啟用 --disable-gil\")\n\n    print(\"\\n=== I/O 綁定任務 (模擬網路請求) ===\")\n    print(\"多線程 (Multithreading):\")\n    run_multithreading(n_tasks, task_type=\"io\")\n\n    print(\"\\n多進程 (Multiprocessing):\")\n    run_multiprocessing(n_tasks, task_type=\"io\")\n\n    print(\"\\nAsyncio:\")\n    asyncio.run(run_asyncio(n_tasks))\n\n    if sys.version_info >= (3, 13) and hasattr(sys, \"disable_gil\"):\n        print(\"\\n禁用 GIL 的多線程:\")\n        run_nogil_multithreading(n_tasks, task_type=\"io\")\n    else:\n        print(\"\\n禁用 GIL 的多線程: 需要 Python 3.13 並啟用 --disable-gil\")\n","python",[115,535,536,544,549,555,561,567,573,579,585,591,597,603,609,615,621,627,633,639,645,650,656,662,668,674,680,685,691,697,703,708,714,720,726,732,737,743,749,755,761,767,773,779,785,791,797,803,809,814,820,825,830,835,840,845,851,856,862,868,873,878,884,890,896,902,908,913,918,924,929,934,939,944,949,955,960,966,972,978,983,989,994,1000,1006,1012,1018,1024,1030,1036,1042,1047,1053,1059,1065,1070,1076,1082,1088,1093,1099,1105,1110,1116,1122,1128,1134,1140,1145,1151,1156,1162,1167,1172,1178,1183,1189,1195,1200,1205,1210,1216,1221],{"__ignoreMap":185},[537,538,541],"span",{"class":539,"line":540},"line",1,[537,542,543],{},"import time\n",[537,545,546],{"class":539,"line":186},[537,547,548],{},"import threading\n",[537,550,552],{"class":539,"line":551},3,[537,553,554],{},"import multiprocessing\n",[537,556,558],{"class":539,"line":557},4,[537,559,560],{},"import asyncio\n",[537,562,564],{"class":539,"line":563},5,[537,565,566],{},"import sys\n",[537,568,570],{"class":539,"line":569},6,[537,571,572],{},"from concurrent.futures import ThreadPoolExecutor\n",[537,574,576],{"class":539,"line":575},7,[537,577,578],{},"from functools import wraps\n",[537,580,582],{"class":539,"line":581},8,[537,583,584],{"emptyLinePlaceholder":200},"\n",[537,586,588],{"class":539,"line":587},9,[537,589,590],{},"# 計時裝飾器，用於測量執行時間\n",[537,592,594],{"class":539,"line":593},10,[537,595,596],{},"def timing_decorator(func):\n",[537,598,600],{"class":539,"line":599},11,[537,601,602],{},"    @wraps(func)\n",[537,604,606],{"class":539,"line":605},12,[537,607,608],{},"    def wrapper(*args, **kwargs):\n",[537,610,612],{"class":539,"line":611},13,[537,613,614],{},"        start = time.time()\n",[537,616,618],{"class":539,"line":617},14,[537,619,620],{},"        result = func(*args, **kwargs)\n",[537,622,624],{"class":539,"line":623},15,[537,625,626],{},"        end = time.time()\n",[537,628,630],{"class":539,"line":629},16,[537,631,632],{},"        print(f\"{func.__name__} 執行時間: {end - start:.4f} 秒\")\n",[537,634,636],{"class":539,"line":635},17,[537,637,638],{},"        return result\n",[537,640,642],{"class":539,"line":641},18,[537,643,644],{},"    return wrapper\n",[537,646,648],{"class":539,"line":647},19,[537,649,584],{"emptyLinePlaceholder":200},[537,651,653],{"class":539,"line":652},20,[537,654,655],{},"# CPU 密集型任務：計算 Fibonacci 數列\n",[537,657,659],{"class":539,"line":658},21,[537,660,661],{},"def fib(n):\n",[537,663,665],{"class":539,"line":664},22,[537,666,667],{},"    if n \u003C= 1:\n",[537,669,671],{"class":539,"line":670},23,[537,672,673],{},"        return n\n",[537,675,677],{"class":539,"line":676},24,[537,678,679],{},"    return fib(n - 1) + fib(n - 2)\n",[537,681,683],{"class":539,"line":682},25,[537,684,584],{"emptyLinePlaceholder":200},[537,686,688],{"class":539,"line":687},26,[537,689,690],{},"# I/O 綁定任務：模擬網路請求（全局函數）\n",[537,692,694],{"class":539,"line":693},27,[537,695,696],{},"def sync_io_task():\n",[537,698,700],{"class":539,"line":699},28,[537,701,702],{},"    time.sleep(1)  # 模擬 1 秒的 I/O 延遲\n",[537,704,706],{"class":539,"line":705},29,[537,707,584],{"emptyLinePlaceholder":200},[537,709,711],{"class":539,"line":710},30,[537,712,713],{},"# 異步 I/O 任務\n",[537,715,717],{"class":539,"line":716},31,[537,718,719],{},"async def io_bound_task():\n",[537,721,723],{"class":539,"line":722},32,[537,724,725],{},"    await asyncio.sleep(1)  # 模擬 1 秒的 I/O 延遲\n",[537,727,729],{"class":539,"line":728},33,[537,730,731],{},"    return \"I/O 任務完成\"\n",[537,733,735],{"class":539,"line":734},34,[537,736,584],{"emptyLinePlaceholder":200},[537,738,740],{"class":539,"line":739},35,[537,741,742],{},"# 多線程實現\n",[537,744,746],{"class":539,"line":745},36,[537,747,748],{},"@timing_decorator\n",[537,750,752],{"class":539,"line":751},37,[537,753,754],{},"def run_multithreading(n_tasks, task_type=\"cpu\"):\n",[537,756,758],{"class":539,"line":757},38,[537,759,760],{},"    threads = []\n",[537,762,764],{"class":539,"line":763},39,[537,765,766],{},"    if task_type == \"cpu\":\n",[537,768,770],{"class":539,"line":769},40,[537,771,772],{},"        for _ in range(n_tasks):\n",[537,774,776],{"class":539,"line":775},41,[537,777,778],{},"            t = threading.Thread(target=fib, args=(35,))\n",[537,780,782],{"class":539,"line":781},42,[537,783,784],{},"            threads.append(t)\n",[537,786,788],{"class":539,"line":787},43,[537,789,790],{},"            t.start()\n",[537,792,794],{"class":539,"line":793},44,[537,795,796],{},"        for t in threads:\n",[537,798,800],{"class":539,"line":799},45,[537,801,802],{},"            t.join()\n",[537,804,806],{"class":539,"line":805},46,[537,807,808],{},"    else:  # I/O 任務\n",[537,810,812],{"class":539,"line":811},47,[537,813,772],{},[537,815,817],{"class":539,"line":816},48,[537,818,819],{},"            t = threading.Thread(target=sync_io_task)\n",[537,821,823],{"class":539,"line":822},49,[537,824,784],{},[537,826,828],{"class":539,"line":827},50,[537,829,790],{},[537,831,833],{"class":539,"line":832},51,[537,834,796],{},[537,836,838],{"class":539,"line":837},52,[537,839,802],{},[537,841,843],{"class":539,"line":842},53,[537,844,584],{"emptyLinePlaceholder":200},[537,846,848],{"class":539,"line":847},54,[537,849,850],{},"# 多進程實現\n",[537,852,854],{"class":539,"line":853},55,[537,855,748],{},[537,857,859],{"class":539,"line":858},56,[537,860,861],{},"def run_multiprocessing(n_tasks, task_type=\"cpu\"):\n",[537,863,865],{"class":539,"line":864},57,[537,866,867],{},"    processes = []\n",[537,869,871],{"class":539,"line":870},58,[537,872,766],{},[537,874,876],{"class":539,"line":875},59,[537,877,772],{},[537,879,881],{"class":539,"line":880},60,[537,882,883],{},"            p = multiprocessing.Process(target=fib, args=(35,))\n",[537,885,887],{"class":539,"line":886},61,[537,888,889],{},"            processes.append(p)\n",[537,891,893],{"class":539,"line":892},62,[537,894,895],{},"            p.start()\n",[537,897,899],{"class":539,"line":898},63,[537,900,901],{},"        for p in processes:\n",[537,903,905],{"class":539,"line":904},64,[537,906,907],{},"            p.join()\n",[537,909,911],{"class":539,"line":910},65,[537,912,808],{},[537,914,916],{"class":539,"line":915},66,[537,917,772],{},[537,919,921],{"class":539,"line":920},67,[537,922,923],{},"            p = multiprocessing.Process(target=sync_io_task)\n",[537,925,927],{"class":539,"line":926},68,[537,928,889],{},[537,930,932],{"class":539,"line":931},69,[537,933,895],{},[537,935,937],{"class":539,"line":936},70,[537,938,901],{},[537,940,942],{"class":539,"line":941},71,[537,943,907],{},[537,945,947],{"class":539,"line":946},72,[537,948,584],{"emptyLinePlaceholder":200},[537,950,952],{"class":539,"line":951},73,[537,953,954],{},"# Asyncio 實現\n",[537,956,958],{"class":539,"line":957},74,[537,959,748],{},[537,961,963],{"class":539,"line":962},75,[537,964,965],{},"async def run_asyncio(n_tasks):\n",[537,967,969],{"class":539,"line":968},76,[537,970,971],{},"    tasks = [io_bound_task() for _ in range(n_tasks)]\n",[537,973,975],{"class":539,"line":974},77,[537,976,977],{},"    await asyncio.gather(*tasks)\n",[537,979,981],{"class":539,"line":980},78,[537,982,584],{"emptyLinePlaceholder":200},[537,984,986],{"class":539,"line":985},79,[537,987,988],{},"# 禁用 GIL 的多線程實現（需 Python 3.13 --disable-gil）\n",[537,990,992],{"class":539,"line":991},80,[537,993,748],{},[537,995,997],{"class":539,"line":996},81,[537,998,999],{},"def run_nogil_multithreading(n_tasks, task_type=\"cpu\"):\n",[537,1001,1003],{"class":539,"line":1002},82,[537,1004,1005],{},"    with ThreadPoolExecutor(max_workers=n_tasks) as executor:\n",[537,1007,1009],{"class":539,"line":1008},83,[537,1010,1011],{},"        if task_type == \"cpu\":\n",[537,1013,1015],{"class":539,"line":1014},84,[537,1016,1017],{},"            futures = [executor.submit(fib, 35) for _ in range(n_tasks)]\n",[537,1019,1021],{"class":539,"line":1020},85,[537,1022,1023],{},"        else:  # I/O 任務\n",[537,1025,1027],{"class":539,"line":1026},86,[537,1028,1029],{},"            futures = [executor.submit(sync_io_task) for _ in range(n_tasks)]\n",[537,1031,1033],{"class":539,"line":1032},87,[537,1034,1035],{},"        for future in futures:\n",[537,1037,1039],{"class":539,"line":1038},88,[537,1040,1041],{},"            future.result()\n",[537,1043,1045],{"class":539,"line":1044},89,[537,1046,584],{"emptyLinePlaceholder":200},[537,1048,1050],{"class":539,"line":1049},90,[537,1051,1052],{},"# 主程式\n",[537,1054,1056],{"class":539,"line":1055},91,[537,1057,1058],{},"if __name__ == \"__main__\":\n",[537,1060,1062],{"class":539,"line":1061},92,[537,1063,1064],{},"    n_tasks = 4  # 任務數量\n",[537,1066,1068],{"class":539,"line":1067},93,[537,1069,584],{"emptyLinePlaceholder":200},[537,1071,1073],{"class":539,"line":1072},94,[537,1074,1075],{},"    print(\"=== CPU 密集型任務 (計算 Fibonacci 數列) ===\")\n",[537,1077,1079],{"class":539,"line":1078},95,[537,1080,1081],{},"    print(\"多線程 (Multithreading):\")\n",[537,1083,1085],{"class":539,"line":1084},96,[537,1086,1087],{},"    run_multithreading(n_tasks, task_type=\"cpu\")\n",[537,1089,1091],{"class":539,"line":1090},97,[537,1092,584],{"emptyLinePlaceholder":200},[537,1094,1096],{"class":539,"line":1095},98,[537,1097,1098],{},"    print(\"\\n多進程 (Multiprocessing):\")\n",[537,1100,1102],{"class":539,"line":1101},99,[537,1103,1104],{},"    run_multiprocessing(n_tasks, task_type=\"cpu\")\n",[537,1106,1108],{"class":539,"line":1107},100,[537,1109,584],{"emptyLinePlaceholder":200},[537,1111,1113],{"class":539,"line":1112},101,[537,1114,1115],{},"    if sys.version_info >= (3, 13) and hasattr(sys, \"disable_gil\"):\n",[537,1117,1119],{"class":539,"line":1118},102,[537,1120,1121],{},"        print(\"\\n禁用 GIL 的多線程:\")\n",[537,1123,1125],{"class":539,"line":1124},103,[537,1126,1127],{},"        run_nogil_multithreading(n_tasks, task_type=\"cpu\")\n",[537,1129,1131],{"class":539,"line":1130},104,[537,1132,1133],{},"    else:\n",[537,1135,1137],{"class":539,"line":1136},105,[537,1138,1139],{},"        print(\"\\n禁用 GIL 的多線程: 需要 Python 3.13 並啟用 --disable-gil\")\n",[537,1141,1143],{"class":539,"line":1142},106,[537,1144,584],{"emptyLinePlaceholder":200},[537,1146,1148],{"class":539,"line":1147},107,[537,1149,1150],{},"    print(\"\\n=== I/O 綁定任務 (模擬網路請求) ===\")\n",[537,1152,1154],{"class":539,"line":1153},108,[537,1155,1081],{},[537,1157,1159],{"class":539,"line":1158},109,[537,1160,1161],{},"    run_multithreading(n_tasks, task_type=\"io\")\n",[537,1163,1165],{"class":539,"line":1164},110,[537,1166,584],{"emptyLinePlaceholder":200},[537,1168,1170],{"class":539,"line":1169},111,[537,1171,1098],{},[537,1173,1175],{"class":539,"line":1174},112,[537,1176,1177],{},"    run_multiprocessing(n_tasks, task_type=\"io\")\n",[537,1179,1181],{"class":539,"line":1180},113,[537,1182,584],{"emptyLinePlaceholder":200},[537,1184,1186],{"class":539,"line":1185},114,[537,1187,1188],{},"    print(\"\\nAsyncio:\")\n",[537,1190,1192],{"class":539,"line":1191},115,[537,1193,1194],{},"    asyncio.run(run_asyncio(n_tasks))\n",[537,1196,1198],{"class":539,"line":1197},116,[537,1199,584],{"emptyLinePlaceholder":200},[537,1201,1203],{"class":539,"line":1202},117,[537,1204,1115],{},[537,1206,1208],{"class":539,"line":1207},118,[537,1209,1121],{},[537,1211,1213],{"class":539,"line":1212},119,[537,1214,1215],{},"        run_nogil_multithreading(n_tasks, task_type=\"io\")\n",[537,1217,1219],{"class":539,"line":1218},120,[537,1220,1133],{},[537,1222,1224],{"class":539,"line":1223},121,[537,1225,1139],{},[11,1227,1228],{"id":1228},"執行結果分析",[529,1230,1234],{"className":1231,"code":1232,"language":1233,"meta":185,"style":185},"language-bash shiki shiki-themes github-light github-dark","$ uv run --no-project --python 3.13.5+freethreaded test.py\n\n=== CPU 密集型任務 (計算 Fibonacci 數列) ===\n多線程 (Multithreading):\nrun_multithreading 執行時間: 0.8912 秒\n\n多進程 (Multiprocessing):\nrun_multiprocessing 執行時間: 1.0846 秒\n\n禁用 GIL 的多線程:\nGIL 狀態: 禁用\nrun_nogil_multithreading 執行時間: 0.8713 秒\n\n=== I/O 綁定任務 (模擬網路請求) ===\n多線程 (Multithreading):\nrun_multithreading 執行時間: 1.0072 秒\n\n多進程 (Multiprocessing):\nrun_multiprocessing 執行時間: 1.1046 秒\n\nAsyncio:\nrun_asyncio 執行時間: 0.0000 秒\n\n禁用 GIL 的多線程:\nGIL 狀態: 禁用\nrun_nogil_multithreading 執行時間: 1.0093 秒\n","bash",[115,1235,1236,1262,1266,1290,1298,1312,1316,1324,1336,1340,1351,1362,1374,1378,1391,1397,1408,1412,1418,1429,1433,1438,1450,1454,1462,1470],{"__ignoreMap":185},[537,1237,1238,1242,1246,1249,1253,1256,1259],{"class":539,"line":540},[537,1239,1241],{"class":1240},"sScJk","$",[537,1243,1245],{"class":1244},"sZZnC"," uv",[537,1247,1248],{"class":1244}," run",[537,1250,1252],{"class":1251},"sj4cs"," --no-project",[537,1254,1255],{"class":1251}," --python",[537,1257,1258],{"class":1244}," 3.13.5+freethreaded",[537,1260,1261],{"class":1244}," test.py\n",[537,1263,1264],{"class":539,"line":186},[537,1265,584],{"emptyLinePlaceholder":200},[537,1267,1268,1271,1274,1277,1281,1284,1287],{"class":539,"line":551},[537,1269,1270],{"class":1244},"===",[537,1272,1273],{"class":1244}," CPU",[537,1275,1276],{"class":1244}," 密集型任務",[537,1278,1280],{"class":1279},"sVt8B"," (計算 ",[537,1282,1283],{"class":1244},"Fibonacci",[537,1285,1286],{"class":1244}," 數列",[537,1288,1289],{"class":1279},") ===\n",[537,1291,1292,1295],{"class":539,"line":557},[537,1293,1294],{"class":1240},"多線程",[537,1296,1297],{"class":1279}," (Multithreading):\n",[537,1299,1300,1303,1306,1309],{"class":539,"line":563},[537,1301,1302],{"class":1240},"run_multithreading",[537,1304,1305],{"class":1244}," 執行時間:",[537,1307,1308],{"class":1251}," 0.8912",[537,1310,1311],{"class":1244}," 秒\n",[537,1313,1314],{"class":539,"line":569},[537,1315,584],{"emptyLinePlaceholder":200},[537,1317,1318,1321],{"class":539,"line":575},[537,1319,1320],{"class":1240},"多進程",[537,1322,1323],{"class":1279}," (Multiprocessing):\n",[537,1325,1326,1329,1331,1334],{"class":539,"line":581},[537,1327,1328],{"class":1240},"run_multiprocessing",[537,1330,1305],{"class":1244},[537,1332,1333],{"class":1251}," 1.0846",[537,1335,1311],{"class":1244},[537,1337,1338],{"class":539,"line":587},[537,1339,584],{"emptyLinePlaceholder":200},[537,1341,1342,1345,1348],{"class":539,"line":593},[537,1343,1344],{"class":1240},"禁用",[537,1346,1347],{"class":1244}," GIL",[537,1349,1350],{"class":1244}," 的多線程:\n",[537,1352,1353,1356,1359],{"class":539,"line":599},[537,1354,1355],{"class":1240},"GIL",[537,1357,1358],{"class":1244}," 狀態:",[537,1360,1361],{"class":1244}," 禁用\n",[537,1363,1364,1367,1369,1372],{"class":539,"line":605},[537,1365,1366],{"class":1240},"run_nogil_multithreading",[537,1368,1305],{"class":1244},[537,1370,1371],{"class":1251}," 0.8713",[537,1373,1311],{"class":1244},[537,1375,1376],{"class":539,"line":611},[537,1377,584],{"emptyLinePlaceholder":200},[537,1379,1380,1382,1385,1388],{"class":539,"line":617},[537,1381,1270],{"class":1244},[537,1383,1384],{"class":1244}," I/O",[537,1386,1387],{"class":1244}," 綁定任務",[537,1389,1390],{"class":1279}," (模擬網路請求) ===\n",[537,1392,1393,1395],{"class":539,"line":623},[537,1394,1294],{"class":1240},[537,1396,1297],{"class":1279},[537,1398,1399,1401,1403,1406],{"class":539,"line":629},[537,1400,1302],{"class":1240},[537,1402,1305],{"class":1244},[537,1404,1405],{"class":1251}," 1.0072",[537,1407,1311],{"class":1244},[537,1409,1410],{"class":539,"line":635},[537,1411,584],{"emptyLinePlaceholder":200},[537,1413,1414,1416],{"class":539,"line":641},[537,1415,1320],{"class":1240},[537,1417,1323],{"class":1279},[537,1419,1420,1422,1424,1427],{"class":539,"line":647},[537,1421,1328],{"class":1240},[537,1423,1305],{"class":1244},[537,1425,1426],{"class":1251}," 1.1046",[537,1428,1311],{"class":1244},[537,1430,1431],{"class":539,"line":652},[537,1432,584],{"emptyLinePlaceholder":200},[537,1434,1435],{"class":539,"line":658},[537,1436,1437],{"class":1240},"Asyncio:\n",[537,1439,1440,1443,1445,1448],{"class":539,"line":664},[537,1441,1442],{"class":1240},"run_asyncio",[537,1444,1305],{"class":1244},[537,1446,1447],{"class":1251}," 0.0000",[537,1449,1311],{"class":1244},[537,1451,1452],{"class":539,"line":670},[537,1453,584],{"emptyLinePlaceholder":200},[537,1455,1456,1458,1460],{"class":539,"line":676},[537,1457,1344],{"class":1240},[537,1459,1347],{"class":1244},[537,1461,1350],{"class":1244},[537,1463,1464,1466,1468],{"class":539,"line":682},[537,1465,1355],{"class":1240},[537,1467,1358],{"class":1244},[537,1469,1361],{"class":1244},[537,1471,1472,1474,1476,1479],{"class":539,"line":687},[537,1473,1366],{"class":1240},[537,1475,1305],{"class":1244},[537,1477,1478],{"class":1251}," 1.0093",[537,1480,1311],{"class":1244},[231,1482,1484],{"id":1483},"cpu-密集型任務計算-fibonacci-數列","CPU 密集型任務（計算 Fibonacci 數列）",[15,1486,1487,1488],{},"執行時間排序：",[31,1489,1490],{},"GIL Disabled (0.8713 秒) \u003C 多線程 (0.8912 秒) \u003C 多進程 (1.0846 秒)",[100,1492,1493,1498,1503],{},[103,1494,1495,1497],{},[31,1496,1294],{},"：受 GIL 限制，執行速度與單 CPU 執行差不多",[103,1499,1500,1502],{},[31,1501,1320],{},"：運用多個 CPU 核心加速",[103,1504,1505,1508],{},[31,1506,1507],{},"GIL Disabled","：運用多個 CPU 核心加速，且避免了多進程管理的額外開銷",[231,1510,1512],{"id":1511},"io-綁定任務模擬網路請求","I/O 綁定任務（模擬網路請求）",[15,1514,1487,1515],{},[31,1516,1517],{},"Asyncio (0.0000 秒) \u003C\u003C 多線程 (1.0072 秒) ≈ GIL Disabled (1.0093 秒) ≈ 多進程 (1.1046 秒)",[100,1519,1520,1526],{},[103,1521,1522,1525],{},[31,1523,1524],{},"多線程、多進程、GIL Disabled","：三者效能差不多",[103,1527,1528,1531,1532,1535],{},[31,1529,1530],{},"Asyncio","：效能最好，因為測試任務使用 ",[115,1533,1534],{},"await asyncio.sleep","，基本上不會有運行時間開銷",[11,1537,1538],{"id":1538},"實際應用場景",[15,1540,1541],{},[31,1542,1543],{},"首先需要考慮業務場景，做這個優化加速是否有需要？如果加速沒有很重要的話就是一個 Nice to have 的優化。",[231,1545,1547],{"id":1546},"不建議使用-disabled-gil","不建議使用 Disabled GIL",[15,1549,1550],{},"目前 Disabled GIL 還在測試階段，許多套件都還沒完全支援，也需要自己處理變數管理等問題，目前不考慮使用。",[231,1552,1554],{"id":1553},"適合使用-multiprocessing-的場景","適合使用 Multiprocessing 的場景",[15,1556,1557],{},"非 AI 的大量運算需求，可以使用 Multiprocessing 來加速：",[100,1559,1560,1566],{},[103,1561,1562,1565],{},[31,1563,1564],{},"售電平台的最佳參數求取","：目前策略使用網格搜索，可以切分網格分配給 CPU 執行",[103,1567,1568,1571],{},[31,1569,1570],{},"EV 管理平台","：需要定時計算電站使用率",[231,1573,1575],{"id":1574},"適合使用-asyncio-的場景","適合使用 Asyncio 的場景",[15,1577,1578],{},"網路的大量需求，可以使用 Asyncio 來加速：",[100,1580,1581,1587,1605,1615],{},[103,1582,1583,1586],{},[31,1584,1585],{},"MQTT / OCPP","：這類 pub/sub 協議",[103,1588,1589,336,1592],{},[31,1590,1591],{},"爬蟲或大量 Third-party API 請求",[100,1593,1594],{},[103,1595,1596,1597,1600,1601,1604],{},"使用 ",[115,1598,1599],{},"aiohttp"," 替換 ",[115,1602,1603],{},"requests"," 模組",[103,1606,1607,336,1610],{},[31,1608,1609],{},"Database 異步操作",[100,1611,1612],{},[103,1613,1614],{},"pymongo 遷移到 async client (≥ v4.13)",[103,1616,1617,336,1620],{},[31,1618,1619],{},"Web Framework",[100,1621,1622],{},[103,1623,1624],{},"Flask 遷移到 FastAPI",[11,1626,1627],{"id":1627},"參考資料",[100,1629,1630,1637,1644],{},[103,1631,1632],{},[53,1633,1636],{"href":1634,"rel":1635},"https://www.youtube.com/watch?v=brYsDi-JajI",[57],"【python】asyncio的理解與入門，搞不明白協程？看這個視頻就夠了",[103,1638,1639],{},[53,1640,1643],{"href":1641,"rel":1642},"https://www.youtube.com/watch?v=K0BjgYZbgfE&t=94s",[57],"【python】await機制詳解。再來個硬核內容，把並行和依賴背後的原理全給你講明白",[103,1645,1646],{},[53,1647,1650],{"href":1648,"rel":1649},"https://pymongo.readthedocs.io/en/stable/async-tutorial.html",[57],"Async Tutorial - PyMongo 4.14.1 documentation",[1652,1653,1654],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":185,"searchDepth":186,"depth":186,"links":1656},[1657,1658,1664,1665,1666,1670,1675],{"id":219,"depth":186,"text":220},{"id":229,"depth":186,"text":229,"children":1659},[1660,1661,1662,1663],{"id":233,"depth":551,"text":234},{"id":284,"depth":551,"text":285},{"id":323,"depth":551,"text":324},{"id":360,"depth":551,"text":361},{"id":382,"depth":186,"text":382},{"id":527,"depth":186,"text":527},{"id":1228,"depth":186,"text":1228,"children":1667},[1668,1669],{"id":1483,"depth":551,"text":1484},{"id":1511,"depth":551,"text":1512},{"id":1538,"depth":186,"text":1538,"children":1671},[1672,1673,1674],{"id":1546,"depth":551,"text":1547},{"id":1553,"depth":551,"text":1554},{"id":1574,"depth":551,"text":1575},{"id":1627,"depth":186,"text":1627},"技術","2025-10-24","介紹 Python 並發處理方法，包括多線程、多進程、asyncio 以及其實務應用。",{},"/blog/concurrency",{"title":214,"description":1678},"blog/concurrency",[208,1684,1685,1530],"Concurrency","Threading","1DEoTJ_oFj-_n1U7PDKbn7wPsTmsBUK-5vAp6nZ_s0E",{"id":1688,"title":1689,"author":6,"body":1690,"category":1676,"date":1677,"description":2075,"draft":196,"extension":197,"image":185,"meta":2076,"navigation":200,"path":2077,"seo":2078,"stem":2079,"tags":2080,"__hash__":2081},"blog/blog/python-gil-introduction.md","Python GIL 介紹",{"type":8,"value":1691,"toc":2063},[1692,1696,1701,1704,1724,1727,1731,1736,1740,1760,1764,1767,1843,1847,1850,1918,1922,1925,1949,1952,1972,1975,2031,2033],[11,1693,1695],{"id":1694},"什麼是-gil","什麼是 GIL？",[157,1697,1698],{},[15,1699,1700],{},"💡 GIL 是 CPython 解釋器中的一個鎖，確保在任何時刻只有一個線程可以執行 Python 字節碼。",[15,1702,1703],{},"這意味著即使有多個線程，Python 程式也無法真正利用多核心 CPU 實現並行（parallelism），僅能實現並發（concurrency）的假象。GIL 為 Python 提供了以下好處：",[100,1705,1706,1712,1718],{},[103,1707,1708,1711],{},[31,1709,1710],{},"簡化種族條件（Race Conditions）","：防止Multi thread同時修改共享資料，降低記憶體損壞風險。",[103,1713,1714,1717],{},[31,1715,1716],{},"簡化垃圾回收（Garbage Collection）","：GIL 使記憶體管理更簡單，無需複雜的鎖機制。",[103,1719,1720,1723],{},[31,1721,1722],{},"歷史背景","：Python 於 1991 年設計時，多數電腦僅有單核心 CPU，GIL 是當時簡化實現的合理選擇。",[15,1725,1726],{},"然而，GIL 的缺點顯而易見：它限制了多核心 CPU 的利用率，使 Python 在 CPU 密集型任務（如數學計算、資料解析、AI 模型訓練）上的效能不如預期。",[11,1728,1730],{"id":1729},"為什麼移除-gilpep-703-python-313","為什麼移除 GIL？（PEP 703, Python 3.13）",[157,1732,1733],{},[15,1734,1735],{},"💡 隨著硬體技術的進步（多核心 CPU 普及）和 Python 社群對高效能的需求，GIL 逐漸成為瓶頸。PEP 703（Python 3.13）提出使 GIL 可選，允許程式在編譯時或運行時禁用 GIL，讓Multi thread直接利用操作系統的線程調度器，實現真正的並行。",[231,1737,1739],{"id":1738},"移除-gil-的動機","移除 GIL 的動機",[100,1741,1742,1748,1754],{},[103,1743,1744,1747],{},[31,1745,1746],{},"社群需求","：開發者希望 Python 能更快，並充分利用現代多核心硬體。",[103,1749,1750,1753],{},[31,1751,1752],{},"效能提升","：禁用 GIL 後，某些 CPU 密集型任務（如桶排序、分形生成）可顯著加速。",[103,1755,1756,1759],{},[31,1757,1758],{},"競爭壓力","：其他語言（如 Go、Rust）支援真正的並行，Python 需跟上時代。",[231,1761,1763],{"id":1762},"移除-gil-的挑戰","移除 GIL 的挑戰",[15,1765,1766],{},"移除 GIL 並非易事，因為 Python 的許多核心機制依賴 GIL 的保護。以下是主要挑戰：",[1768,1769,1770,1802,1814,1831],"ol",{},[103,1771,1772,1775],{},[31,1773,1774],{},"引用計數（Reference Counting）",[100,1776,1777,1783],{},[103,1778,1779,1782],{},[31,1780,1781],{},"問題","：傳統的非原子引用計數（non-atomic reference counting）不具線程安全，可能導致種族條件。例如，refcount++ 分三步（讀、加、寫），可能被其他線程中斷。",[103,1784,1785,336,1788],{},[31,1786,1787],{},"解決方案",[100,1789,1790,1796],{},[103,1791,1792,1795],{},[31,1793,1794],{},"原子引用計數","：線程安全，但速度慢 10x-100x。",[103,1797,1798,1801],{},[31,1799,1800],{},"偏向引用計數（Biased Reference Counting）","：檢查引用是否僅屬於單一線程，若是則使用快速的非原子計數，否則使用原子計數。",[103,1803,1804,1807],{},[31,1805,1806],{},"垃圾回收（Garbage Collection）",[100,1808,1809],{},[103,1810,1811,1813],{},[31,1812,1781],{},"：傳統垃圾回收依賴 GIL 保護，需改用延遲引用計數（deferred reference counting）來處理循環引用。",[103,1815,1816,1819],{},[31,1817,1818],{},"記憶體分配",[100,1820,1821,1826],{},[103,1822,1823,1825],{},[31,1824,1781],{},"：現有記憶體分配器假設 GIL 保護，不具線程安全。",[103,1827,1828,1830],{},[31,1829,1787],{},"：開發新的線程安全記憶體分配器，優化列表和字典的快速讀取。",[103,1832,1833,1836],{},[31,1834,1835],{},"兼容性",[100,1837,1838],{},[103,1839,1840,1842],{},[31,1841,1781],{},"：許多 C-API 擴展（如 NumPy、Pandas）假設 GIL 存在，需重新編譯以支援無 GIL 環境。",[11,1844,1846],{"id":1845},"gil-與禁用-gil-的比較","GIL 與禁用 GIL 的比較",[15,1848,1849],{},"以下表格比較了 GIL 和禁用 GIL 的特點：",[19,1851,1852,1864],{},[22,1853,1854],{},[25,1855,1856,1858,1861],{},[28,1857,391],{},[28,1859,1860],{},"啟用 GIL",[28,1862,1863],{},"禁用 GIL",[45,1865,1866,1879,1892,1905],{},[25,1867,1868,1873,1876],{},[50,1869,1870],{},[31,1871,1872],{},"並行性",[50,1874,1875],{},"非真正的並行，僅一個線程執行",[50,1877,1878],{},"真正的Multi thread並行，利用多核心",[25,1880,1881,1886,1889],{},[50,1882,1883],{},[31,1884,1885],{},"資料共享",[50,1887,1888],{},"簡單，無需額外鎖",[50,1890,1891],{},"需小心處理種族條件（如使用 threading.Lock）",[25,1893,1894,1899,1902],{},[50,1895,1896],{},[31,1897,1898],{},"效能",[50,1900,1901],{},"受限於 GIL，CPU 綁定任務慢",[50,1903,1904],{},"視程式而定，可能顯著提升（如桶排序、分形生成）",[25,1906,1907,1912,1915],{},[50,1908,1909],{},[31,1910,1911],{},"擴展相容性",[50,1913,1914],{},"廣泛支援",[50,1916,1917],{},"需檢查擴展版本（如 Cython、NumPy）",[11,1919,1921],{"id":1920},"禁用-gil-的推薦場景","禁用 GIL 的推薦場景",[15,1923,1924],{},"禁用 GIL 在以下場景中特別有用：",[100,1926,1927,1933,1938,1943],{},[103,1928,1929,1932],{},[31,1930,1931],{},"ETL 處理","：資料提取、轉換和載入需要大量計算。",[103,1934,1935,1937],{},[31,1936,307],{},"：如濾波、轉換等 CPU 密集型操作。",[103,1939,1940,1942],{},[31,1941,310],{},"：處理大量資料並進行模式匹配。",[103,1944,1945,1948],{},[31,1946,1947],{},"即時分析","：需要快速響應的計算任務。",[11,1950,1951],{"id":1951},"注意事項",[100,1953,1954,1960,1966],{},[103,1955,1956,1959],{},[31,1957,1958],{},"Race Condition","：禁用 GIL 後，Multi thread可能同時存取共享資料，導致記憶體損壞。建議使用 threading.Lock 或其他同步機制。",[103,1961,1962,1965],{},[31,1963,1964],{},"擴展模組相容性","：確認使用的庫（如 NumPy、Pandas、PyTorch）支援無 GIL 環境。",[103,1967,1968,1971],{},[31,1969,1970],{},"效能不確定性","：禁用 GIL 不保證所有程式都變快，需針對具體任務進行基準測試。",[11,1973,1974],{"id":1974},"常見誤解與解答",[1768,1976,1977,1991,2001,2011,2021],{},[103,1978,1979,1982,1983],{},[31,1980,1981],{},"並發(Concurrency)與並行(Parallelism)的區別","：",[100,1984,1985,1988],{},[103,1986,1987],{},"並發是多任務「看似」同時進行，實際上可能是切換執行。",[103,1989,1990],{},"並行是多任務在多核心上真正同時執行。",[103,1992,1993,1996],{},[31,1994,1995],{},"為什麼 Python 慢？",[100,1997,1998],{},[103,1999,2000],{},"GIL 限制了Multi thread的並行性，導致 CPU 綁定任務無法充分利用多核心。",[103,2002,2003,2006],{},[31,2004,2005],{},"禁用 GIL 如何管理 CPU 使用？",[100,2007,2008],{},[103,2009,2010],{},"透過操作系統的線程調度器，讓Multi thread在多核心上並行執行。",[103,2012,2013,2016],{},[31,2014,2015],{},"禁用 GIL 是否總是更快？",[100,2017,2018],{},[103,2019,2020],{},"不一定，效能提升取決於任務類型和程式設計。某些任務（如 I/O 綁定）可能無明顯改善。",[103,2022,2023,2026],{},[31,2024,2025],{},"簡單資料共享是什麼？",[100,2027,2028],{},[103,2029,2030],{},"指線程間直接存取記憶體的能力，而Multi process需要管道或共享記憶體，較為複雜。",[11,2032,1627],{"id":1627},[100,2034,2035,2042,2049,2056],{},[103,2036,2037],{},[53,2038,2041],{"href":2039,"rel":2040},"https://peps.python.org/pep-0703/",[57],"PEP 703 – Making the Global Interpreter Lock Optional in CPython",[103,2043,2044],{},[53,2045,2048],{"href":2046,"rel":2047},"https://tw.pycon.org/2025/zh-hant/conference/keynotes#Donghee_Na",[57],"PyCon TW 2025 主題演講",[103,2050,2051],{},[53,2052,2055],{"href":2053,"rel":2054},"https://tw.pycon.org/2025/zh-hant/conference/talk/352",[57],"PyCon TW 2025 - Talk 352",[103,2057,2058],{},[53,2059,2062],{"href":2060,"rel":2061},"https://tw.pycon.org/2025/zh-hant/conference/talk/335",[57],"PyCon TW 2025 - Talk 335",{"title":185,"searchDepth":186,"depth":186,"links":2064},[2065,2066,2070,2071,2072,2073,2074],{"id":1694,"depth":186,"text":1695},{"id":1729,"depth":186,"text":1730,"children":2067},[2068,2069],{"id":1738,"depth":551,"text":1739},{"id":1762,"depth":551,"text":1763},{"id":1845,"depth":186,"text":1846},{"id":1920,"depth":186,"text":1921},{"id":1951,"depth":186,"text":1951},{"id":1974,"depth":186,"text":1974},{"id":1627,"depth":186,"text":1627},"Python 全域直譯器鎖（GIL）介紹",{},"/blog/python-gil-introduction",{"title":1689,"description":2075},"blog/python-gil-introduction",[208,1355,1685,1684],"HEyyMIX4ERxJ70UGu2zKfrzTPLf2cYOCJfUly7xneRo",{"id":2083,"title":2084,"author":6,"body":2085,"category":1676,"date":1677,"description":3408,"draft":196,"extension":197,"image":185,"meta":3409,"navigation":200,"path":3410,"seo":3411,"stem":3412,"tags":3413,"__hash__":3417},"blog/blog/uv-and-ruff.md","uv and Ruff: 最佳的 Python 工具鏈",{"type":8,"value":2086,"toc":3390},[2087,2090,2095,2098,2106,2110,2140,2144,2151,2173,2180,2191,2195,2423,2426,2431,2439,2444,2450,2453,2456,2464,2468,2490,2492,2496,2543,2545,2548,2551,2576,2579,2583,2621,2625,2726,2730,2805,2812,2819,3097,3101,3121,3125,3136,3140,3199,3203,3228,3232,3360,3362,3364,3387],[2088,2089,220],"h1",{"id":219},[157,2091,2092],{},[15,2093,2094],{},"若遇過 format on save 很慢 / 裝環境很麻煩 / 套件衝突，那這篇文章可能對你有用",[2088,2096,2097],{"id":2097},"uv",[157,2099,2100,2103],{},[15,2101,2102],{},"uv 是一個極速的 Python 套件管理器和專案管理工具，被設計為 pip、pip-tools、pipx、poetry、pyenv、virtualenv 等工具的統一替代方案。",[15,2104,2105],{},"uv 的核心理念是提供一個快速、可靠且易用的 Python 工具鏈。",[11,2107,2109],{"id":2108},"uv-的主要特點","uv 的主要特點",[100,2111,2112,2117,2130,2137],{},[103,2113,2114],{},[31,2115,2116],{},"速度極快",[103,2118,2119,2122],{},[31,2120,2121],{},"統一的工作流程",[100,2123,2124,2127],{},[103,2125,2126],{},"整合了套件安裝、虛擬環境管理、專案初始化等功能",[103,2128,2129],{},"一個工具解決多個需求，簡化開發工作流程",[103,2131,2132,2133,2136],{},"確保所有環境部署時依賴相同 (",[115,2134,2135],{},"uv.lock",")",[103,2138,2139],{},"方便切換 python 版本，以及在設定檔中鎖定 python 版本",[11,2141,2143],{"id":2142},"requirementstxt-vs-pyprojecttoml","requirements.txt vs pyproject.toml",[157,2145,2146],{},[15,2147,2148],{},[31,2149,2150],{},"pyproject.toml 是官方標準",[100,2152,2153,2156,2159,2162,2165],{},[103,2154,2155],{},"PEP 518、PEP 621 等官方標準定義的專案配置格式",[103,2157,2158],{},"Python 生態系統的統一標準，未來發展方向",[103,2160,2161],{},"被所有現代工具（pip、uv、poetry、setuptools）支援",[103,2163,2164],{},"可以根據開發、打包、部署等環境分別配置",[103,2166,2167,2168],{},"有 support 的工具都可以統一配置\n",[100,2169,2170],{},[103,2171,2172],{},"例如等等要介紹的 ruff",[157,2174,2175],{},[15,2176,2177],{},[31,2178,2179],{},"requirements.txt 是非正式慣例",[100,2181,2182,2185,2188],{},[103,2183,2184],{},"只是社群約定俗成的做法",[103,2186,2187],{},"沒有正式規範，格式相對簡陋",[103,2189,2190],{},"逐漸被新工具取代",[231,2192,2194],{"id":2193},"範例-pyprojecttoml","範例 pyproject.toml",[529,2196,2200],{"className":2197,"code":2198,"language":2199,"meta":185,"style":185},"language-toml shiki shiki-themes github-light github-dark","[project]\nname = \"my-awesome-project\"\nversion = \"1.0.0\"\ndescription = \"A fantastic Python project\"\nauthors = [{name = \"Your Name\", email = \"you@example.com\"}]\nlicense = {text = \"MIT\"}\nreadme = \"README.md\"\nkeywords = [\"python\", \"awesome\"]\nclassifiers = [\n    \"Development Status :: 4 - Beta\",\n    \"Programming Language :: Python :: 3.11\",\n]\n\n# 依賴管理\ndependencies = [\n    \"requests>=2.28.0\",\n    \"click>=8.0.0\",\n]\n\n# 開發依賴\n[project.optional-dependencies]\ndev = [\n    \"pytest>=7.0.0\",\n    \"ruff>=0.1.0\",\n    \"mypy>=1.0.0\",\n]\ntest = [\n    \"pytest-cov>=4.0.0\",\n    \"pytest-mock>=3.10.0\",\n]\n\n# 工具配置\n[tool.ruff]\nline-length = 88\ntarget-version = \"py311\"\n\n[tool.ruff.lint]\nselect = [\"E\", \"F\", \"UP\", \"B\", \"SIM\", \"I\"]\n\n[tool.pytest.ini_options]\ntestpaths = [\"tests\"]\npython_files = [\"test_*.py\"]\n\n[build-system]\nrequires = [\"setuptools>=61.0\"]\nbuild-backend = \"setuptools.build_meta\"\n","toml",[115,2201,2202,2207,2212,2217,2222,2227,2232,2237,2242,2247,2252,2257,2262,2266,2271,2276,2281,2286,2290,2294,2299,2304,2309,2314,2319,2324,2328,2333,2338,2343,2347,2351,2356,2361,2366,2371,2375,2380,2385,2389,2394,2399,2404,2408,2413,2418],{"__ignoreMap":185},[537,2203,2204],{"class":539,"line":540},[537,2205,2206],{},"[project]\n",[537,2208,2209],{"class":539,"line":186},[537,2210,2211],{},"name = \"my-awesome-project\"\n",[537,2213,2214],{"class":539,"line":551},[537,2215,2216],{},"version = \"1.0.0\"\n",[537,2218,2219],{"class":539,"line":557},[537,2220,2221],{},"description = \"A fantastic Python project\"\n",[537,2223,2224],{"class":539,"line":563},[537,2225,2226],{},"authors = [{name = \"Your Name\", email = \"you@example.com\"}]\n",[537,2228,2229],{"class":539,"line":569},[537,2230,2231],{},"license = {text = \"MIT\"}\n",[537,2233,2234],{"class":539,"line":575},[537,2235,2236],{},"readme = \"README.md\"\n",[537,2238,2239],{"class":539,"line":581},[537,2240,2241],{},"keywords = [\"python\", \"awesome\"]\n",[537,2243,2244],{"class":539,"line":587},[537,2245,2246],{},"classifiers = [\n",[537,2248,2249],{"class":539,"line":593},[537,2250,2251],{},"    \"Development Status :: 4 - Beta\",\n",[537,2253,2254],{"class":539,"line":599},[537,2255,2256],{},"    \"Programming Language :: Python :: 3.11\",\n",[537,2258,2259],{"class":539,"line":605},[537,2260,2261],{},"]\n",[537,2263,2264],{"class":539,"line":611},[537,2265,584],{"emptyLinePlaceholder":200},[537,2267,2268],{"class":539,"line":617},[537,2269,2270],{},"# 依賴管理\n",[537,2272,2273],{"class":539,"line":623},[537,2274,2275],{},"dependencies = [\n",[537,2277,2278],{"class":539,"line":629},[537,2279,2280],{},"    \"requests>=2.28.0\",\n",[537,2282,2283],{"class":539,"line":635},[537,2284,2285],{},"    \"click>=8.0.0\",\n",[537,2287,2288],{"class":539,"line":641},[537,2289,2261],{},[537,2291,2292],{"class":539,"line":647},[537,2293,584],{"emptyLinePlaceholder":200},[537,2295,2296],{"class":539,"line":652},[537,2297,2298],{},"# 開發依賴\n",[537,2300,2301],{"class":539,"line":658},[537,2302,2303],{},"[project.optional-dependencies]\n",[537,2305,2306],{"class":539,"line":664},[537,2307,2308],{},"dev = [\n",[537,2310,2311],{"class":539,"line":670},[537,2312,2313],{},"    \"pytest>=7.0.0\",\n",[537,2315,2316],{"class":539,"line":676},[537,2317,2318],{},"    \"ruff>=0.1.0\",\n",[537,2320,2321],{"class":539,"line":682},[537,2322,2323],{},"    \"mypy>=1.0.0\",\n",[537,2325,2326],{"class":539,"line":687},[537,2327,2261],{},[537,2329,2330],{"class":539,"line":693},[537,2331,2332],{},"test = [\n",[537,2334,2335],{"class":539,"line":699},[537,2336,2337],{},"    \"pytest-cov>=4.0.0\",\n",[537,2339,2340],{"class":539,"line":705},[537,2341,2342],{},"    \"pytest-mock>=3.10.0\",\n",[537,2344,2345],{"class":539,"line":710},[537,2346,2261],{},[537,2348,2349],{"class":539,"line":716},[537,2350,584],{"emptyLinePlaceholder":200},[537,2352,2353],{"class":539,"line":722},[537,2354,2355],{},"# 工具配置\n",[537,2357,2358],{"class":539,"line":728},[537,2359,2360],{},"[tool.ruff]\n",[537,2362,2363],{"class":539,"line":734},[537,2364,2365],{},"line-length = 88\n",[537,2367,2368],{"class":539,"line":739},[537,2369,2370],{},"target-version = \"py311\"\n",[537,2372,2373],{"class":539,"line":745},[537,2374,584],{"emptyLinePlaceholder":200},[537,2376,2377],{"class":539,"line":751},[537,2378,2379],{},"[tool.ruff.lint]\n",[537,2381,2382],{"class":539,"line":757},[537,2383,2384],{},"select = [\"E\", \"F\", \"UP\", \"B\", \"SIM\", \"I\"]\n",[537,2386,2387],{"class":539,"line":763},[537,2388,584],{"emptyLinePlaceholder":200},[537,2390,2391],{"class":539,"line":769},[537,2392,2393],{},"[tool.pytest.ini_options]\n",[537,2395,2396],{"class":539,"line":775},[537,2397,2398],{},"testpaths = [\"tests\"]\n",[537,2400,2401],{"class":539,"line":781},[537,2402,2403],{},"python_files = [\"test_*.py\"]\n",[537,2405,2406],{"class":539,"line":787},[537,2407,584],{"emptyLinePlaceholder":200},[537,2409,2410],{"class":539,"line":793},[537,2411,2412],{},"[build-system]\n",[537,2414,2415],{"class":539,"line":799},[537,2416,2417],{},"requires = [\"setuptools>=61.0\"]\n",[537,2419,2420],{"class":539,"line":805},[537,2421,2422],{},"build-backend = \"setuptools.build_meta\"\n",[231,2424,2425],{"id":2425},"專案檔案架構比較",[15,2427,2428],{},[31,2429,2430],{},"使用 requirements.txt 的專案結構",[529,2432,2437],{"className":2433,"code":2435,"language":2436},[2434],"language-text","my-project/\n├── requirements.txt\n├── requirements-dev.txt\n├── requirements-test.txt\n├── setup.py        # setuptools 配置\n├── setup.cfg\n├── pytest.ini      # pytest 配置\n├── mypy.ini        # mypy 配置\n├── .coveragerc     # coverage 配置\n└── pyproject.toml  # 只給 ruff 用\n","text",[115,2438,2435],{"__ignoreMap":185},[15,2440,2441],{},[31,2442,2443],{},"使用 pyproject.toml 的專案結構",[529,2445,2448],{"className":2446,"code":2447,"language":2436},[2434],"my-project/\n├── pyproject.toml  # 所有配置都在這裡\n├── uv.lock         # 版本鎖定檔案\n└── src/\n",[115,2449,2447],{"__ignoreMap":185},[2451,2452],"hr",{},[2088,2454,2455],{"id":2455},"ruff",[157,2457,2458,2461],{},[15,2459,2460],{},"ruff 是一個極速的 Python linter 和 code formatter，用 Rust 編寫，可以替代 Flake8、isort、Black 等多個工具。",[15,2462,2463],{},"它的目標是提供最快速、最全面的 Python 程式碼品質檢查和格式化解決方案。",[11,2465,2467],{"id":2466},"ruff-的主要特點","ruff 的主要特點",[100,2469,2470,2472,2485],{},[103,2471,2116],{},[103,2473,2474,2477],{},[31,2475,2476],{},"豐富的規則集",[100,2478,2479,2482],{},[103,2480,2481],{},"支援 800+ 個 lint 規則",[103,2483,2484],{},"整合了 Flake8、isort、pycodestyle、pyflakes 等工具的規則",[103,2486,2487],{},[31,2488,2489],{},"無需配置即可使用",[2451,2491],{},[2088,2493,2495],{"id":2494},"為什麼要使用-uv-和-ruff","為什麼要使用 uv 和 ruff？",[100,2497,2498,2514,2520,2537],{},[103,2499,2500,2503,2504],{},[31,2501,2502],{},"即時程式碼檢查","：ruff 的極速檢查讓程式碼品質控制變得無縫\n",[100,2505,2506],{},[103,2507,2508,2509],{},"尤其公司專案目前很多軟體動輒幾千行（較少抽象與依功能分割檔案），使用傳統程式碼檢查與 format 工具需要接近 1 分鐘或甚至更多\n",[100,2510,2511],{},[103,2512,2513],{},"想像你每次 Ctrl + S 都要等 1 分鐘…",[103,2515,2516,2519],{},[31,2517,2518],{},"穩定的工具鏈","：目前 uv 與 ruff 算是在各自領域（套件管理與 lint check / format）統一江湖的存在，未來不太會遇到需要再更換的情況",[103,2521,2522,2523],{},"為了未來可能需要統一產品版本鋪路，使用 Git 協作的情況會越來越多\n",[100,2524,2525,2531],{},[103,2526,2527,2530],{},[31,2528,2529],{},"一致的環境","：uv 確保所有團隊成員使用相同的依賴版本",[103,2532,2533,2536],{},[31,2534,2535],{},"統一的程式碼風格","：ruff 自動化程式碼風格檢查和格式化",[103,2538,2539,2542],{},[31,2540,2541],{},"更快的 CI/CD","：極速的執行效能減少構建時間，現在也有使用 GKE 的專案，可以減少 runner 開銷",[2451,2544],{},[2088,2546,2547],{"id":2547},"遷移範例",[15,2549,2550],{},"假設原本使用 requirements.txt + 很多 lint error",[100,2552,2553],{},[103,2554,2555,2556],{},"lint error 例如：\n",[100,2557,2558,2563,2566,2569],{},[103,2559,2560],{},[115,2561,2562],{},"if a == None",[103,2564,2565],{},"assign var or import but never used",[103,2567,2568],{},"assign a python keyword as a var name",[103,2570,2571,2572,2575],{},"use ",[115,2573,2574],{},"format"," instead of f-string",[15,2577,2578],{},"有遇到任何問題找 AI 處理，或是看文檔的 best practices",[11,2580,2582],{"id":2581},"_1-安裝與驗證","1. 安裝與驗證",[529,2584,2586],{"className":1231,"code":2585,"language":1233,"meta":185,"style":185},"# 安裝 uv\npip install uv\n\n# 驗證（否則要看一下文檔，根據作業系統有不同安裝方式）\nuv --version\n",[115,2587,2588,2594,2605,2609,2614],{"__ignoreMap":185},[537,2589,2590],{"class":539,"line":540},[537,2591,2593],{"class":2592},"sJ8bj","# 安裝 uv\n",[537,2595,2596,2599,2602],{"class":539,"line":186},[537,2597,2598],{"class":1240},"pip",[537,2600,2601],{"class":1244}," install",[537,2603,2604],{"class":1244}," uv\n",[537,2606,2607],{"class":539,"line":551},[537,2608,584],{"emptyLinePlaceholder":200},[537,2610,2611],{"class":539,"line":557},[537,2612,2613],{"class":2592},"# 驗證（否則要看一下文檔，根據作業系統有不同安裝方式）\n",[537,2615,2616,2618],{"class":539,"line":563},[537,2617,2097],{"class":1240},[537,2619,2620],{"class":1251}," --version\n",[11,2622,2624],{"id":2623},"_2-init-專案與安裝依賴","2. init 專案與安裝依賴",[529,2626,2628],{"className":1231,"code":2627,"language":1233,"meta":185,"style":185},"# 移到專案根目錄\ncd {your_project}\n\n# init 專案\nuv init --no-readme\n\n# 從現有 requirements.txt 遷移\nuv add -r requirements.txt\n\n# 手動新增依賴 (if needed)\nuv add requests numpy\n\n# 安裝 ruff\nuv add --dev ruff\n",[115,2629,2630,2635,2643,2647,2652,2662,2666,2671,2684,2688,2693,2705,2709,2714],{"__ignoreMap":185},[537,2631,2632],{"class":539,"line":540},[537,2633,2634],{"class":2592},"# 移到專案根目錄\n",[537,2636,2637,2640],{"class":539,"line":186},[537,2638,2639],{"class":1251},"cd",[537,2641,2642],{"class":1244}," {your_project}\n",[537,2644,2645],{"class":539,"line":551},[537,2646,584],{"emptyLinePlaceholder":200},[537,2648,2649],{"class":539,"line":557},[537,2650,2651],{"class":2592},"# init 專案\n",[537,2653,2654,2656,2659],{"class":539,"line":563},[537,2655,2097],{"class":1240},[537,2657,2658],{"class":1244}," init",[537,2660,2661],{"class":1251}," --no-readme\n",[537,2663,2664],{"class":539,"line":569},[537,2665,584],{"emptyLinePlaceholder":200},[537,2667,2668],{"class":539,"line":575},[537,2669,2670],{"class":2592},"# 從現有 requirements.txt 遷移\n",[537,2672,2673,2675,2678,2681],{"class":539,"line":581},[537,2674,2097],{"class":1240},[537,2676,2677],{"class":1244}," add",[537,2679,2680],{"class":1251}," -r",[537,2682,2683],{"class":1244}," requirements.txt\n",[537,2685,2686],{"class":539,"line":587},[537,2687,584],{"emptyLinePlaceholder":200},[537,2689,2690],{"class":539,"line":593},[537,2691,2692],{"class":2592},"# 手動新增依賴 (if needed)\n",[537,2694,2695,2697,2699,2702],{"class":539,"line":599},[537,2696,2097],{"class":1240},[537,2698,2677],{"class":1244},[537,2700,2701],{"class":1244}," requests",[537,2703,2704],{"class":1244}," numpy\n",[537,2706,2707],{"class":539,"line":605},[537,2708,584],{"emptyLinePlaceholder":200},[537,2710,2711],{"class":539,"line":611},[537,2712,2713],{"class":2592},"# 安裝 ruff\n",[537,2715,2716,2718,2720,2723],{"class":539,"line":617},[537,2717,2097],{"class":1240},[537,2719,2677],{"class":1244},[537,2721,2722],{"class":1251}," --dev",[537,2724,2725],{"class":1244}," ruff\n",[11,2727,2729],{"id":2728},"_3-設定-python-版本","3. 設定 python 版本",[529,2731,2733],{"className":1231,"code":2732,"language":1233,"meta":185,"style":185},"# 查看可用的 Python 版本\nuv python list\n\n# 範例輸出：\n# cpython-3.14.0b4-macos-aarch64-none                 \u003Cdownload available>\n# cpython-3.14.0b4+freethreaded-macos-aarch64-none    \u003Cdownload available>\n# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3.13\n# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3\n# cpython-3.13.5-macos-aarch64-none                   \u003Cdownload available>\n\n# 設定專案使用的 Python 版本（例如 3.10）\nuv python pin 3.10\n",[115,2734,2735,2740,2750,2754,2759,2764,2769,2774,2779,2784,2788,2793],{"__ignoreMap":185},[537,2736,2737],{"class":539,"line":540},[537,2738,2739],{"class":2592},"# 查看可用的 Python 版本\n",[537,2741,2742,2744,2747],{"class":539,"line":186},[537,2743,2097],{"class":1240},[537,2745,2746],{"class":1244}," python",[537,2748,2749],{"class":1244}," list\n",[537,2751,2752],{"class":539,"line":551},[537,2753,584],{"emptyLinePlaceholder":200},[537,2755,2756],{"class":539,"line":557},[537,2757,2758],{"class":2592},"# 範例輸出：\n",[537,2760,2761],{"class":539,"line":563},[537,2762,2763],{"class":2592},"# cpython-3.14.0b4-macos-aarch64-none                 \u003Cdownload available>\n",[537,2765,2766],{"class":539,"line":569},[537,2767,2768],{"class":2592},"# cpython-3.14.0b4+freethreaded-macos-aarch64-none    \u003Cdownload available>\n",[537,2770,2771],{"class":539,"line":575},[537,2772,2773],{"class":2592},"# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3.13\n",[537,2775,2776],{"class":539,"line":581},[537,2777,2778],{"class":2592},"# cpython-3.13.5-macos-aarch64-none                   /opt/homebrew/bin/python3\n",[537,2780,2781],{"class":539,"line":587},[537,2782,2783],{"class":2592},"# cpython-3.13.5-macos-aarch64-none                   \u003Cdownload available>\n",[537,2785,2786],{"class":539,"line":593},[537,2787,584],{"emptyLinePlaceholder":200},[537,2789,2790],{"class":539,"line":599},[537,2791,2792],{"class":2592},"# 設定專案使用的 Python 版本（例如 3.10）\n",[537,2794,2795,2797,2799,2802],{"class":539,"line":605},[537,2796,2097],{"class":1240},[537,2798,2746],{"class":1244},[537,2800,2801],{"class":1244}," pin",[537,2803,2804],{"class":1251}," 3.10\n",[11,2806,2808,2809],{"id":2807},"_4-設定-pyprojecttoml","4. 設定 ",[115,2810,2811],{},"pyproject.toml",[15,2813,2814,2815,2818],{},"下面只是範例，基本上只需要注意 ",[115,2816,2817],{},"tool.ruff"," 相關的部分",[529,2820,2822],{"className":2197,"code":2821,"language":2199,"meta":185,"style":185},"[project]\nname = \"my-project\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\ndependencies = [\n    \"numpy>=1.24.0\",\n    \"requests>=2.31.0\",\n]\n\n[tool.uv]\ndev-dependencies = [\n    \"black>=23.7.0\",\n    \"flake8>=6.0.0\",\n    \"pytest>=7.4.0\",\n    \"ruff>=0.1.6\",\n]\n\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n# 加上 ruff 相關設置\n[tool.ruff]\nline-length = 88\ntarget-version = \"py310\"  # 改成你的 python 版本\n\n[tool.ruff.lint]\nselect = [\n    \"E\",   # pycodestyle errors\n    \"W\",   # pycodestyle warnings\n    \"F\",   # pyflakes\n    \"I\",   # isort\n    \"C\",   # flake8-comprehensions\n    \"B\",   # flake8-bugbear\n    \"UP\",  # pyupgrade\n]\nignore = [\n    \"E501\",  # line too long, handled by black\n    \"B008\",  # do not perform function calls in argument defaults\n    \"C901\",  # too complex\n    \"W191\",  # indentation contains tabs\n]\nexclude = [\n    \"tests/*\",\n    \"venv/*\",\n    \"*/site-packages/*\",\n    \"*/static/*\",\n    \"wsgi.py\",\n]\nfixable = [\"ALL\"]\nunfixable = []\n\n[tool.ruff.lint.per-file-ignores]\n\"tests/**/*\" = [\"S101\"]  # 測試中允許 assert\n\n[tool.ruff.format]\nquote-style = \"double\"\nindent-style = \"space\"\n",[115,2823,2824,2828,2833,2838,2843,2847,2852,2857,2861,2865,2870,2875,2880,2885,2890,2895,2899,2903,2907,2912,2917,2921,2926,2930,2934,2939,2943,2947,2952,2957,2962,2967,2972,2977,2982,2987,2991,2996,3001,3006,3011,3016,3020,3025,3030,3035,3040,3045,3050,3054,3059,3064,3068,3073,3078,3082,3087,3092],{"__ignoreMap":185},[537,2825,2826],{"class":539,"line":540},[537,2827,2206],{},[537,2829,2830],{"class":539,"line":186},[537,2831,2832],{},"name = \"my-project\"\n",[537,2834,2835],{"class":539,"line":551},[537,2836,2837],{},"version = \"0.1.0\"\n",[537,2839,2840],{"class":539,"line":557},[537,2841,2842],{},"description = \"Add your description here\"\n",[537,2844,2845],{"class":539,"line":563},[537,2846,2275],{},[537,2848,2849],{"class":539,"line":569},[537,2850,2851],{},"    \"numpy>=1.24.0\",\n",[537,2853,2854],{"class":539,"line":575},[537,2855,2856],{},"    \"requests>=2.31.0\",\n",[537,2858,2859],{"class":539,"line":581},[537,2860,2261],{},[537,2862,2863],{"class":539,"line":587},[537,2864,584],{"emptyLinePlaceholder":200},[537,2866,2867],{"class":539,"line":593},[537,2868,2869],{},"[tool.uv]\n",[537,2871,2872],{"class":539,"line":599},[537,2873,2874],{},"dev-dependencies = [\n",[537,2876,2877],{"class":539,"line":605},[537,2878,2879],{},"    \"black>=23.7.0\",\n",[537,2881,2882],{"class":539,"line":611},[537,2883,2884],{},"    \"flake8>=6.0.0\",\n",[537,2886,2887],{"class":539,"line":617},[537,2888,2889],{},"    \"pytest>=7.4.0\",\n",[537,2891,2892],{"class":539,"line":623},[537,2893,2894],{},"    \"ruff>=0.1.6\",\n",[537,2896,2897],{"class":539,"line":629},[537,2898,2261],{},[537,2900,2901],{"class":539,"line":635},[537,2902,584],{"emptyLinePlaceholder":200},[537,2904,2905],{"class":539,"line":641},[537,2906,2412],{},[537,2908,2909],{"class":539,"line":647},[537,2910,2911],{},"requires = [\"hatchling\"]\n",[537,2913,2914],{"class":539,"line":652},[537,2915,2916],{},"build-backend = \"hatchling.build\"\n",[537,2918,2919],{"class":539,"line":658},[537,2920,584],{"emptyLinePlaceholder":200},[537,2922,2923],{"class":539,"line":664},[537,2924,2925],{},"# 加上 ruff 相關設置\n",[537,2927,2928],{"class":539,"line":670},[537,2929,2360],{},[537,2931,2932],{"class":539,"line":676},[537,2933,2365],{},[537,2935,2936],{"class":539,"line":682},[537,2937,2938],{},"target-version = \"py310\"  # 改成你的 python 版本\n",[537,2940,2941],{"class":539,"line":687},[537,2942,584],{"emptyLinePlaceholder":200},[537,2944,2945],{"class":539,"line":693},[537,2946,2379],{},[537,2948,2949],{"class":539,"line":699},[537,2950,2951],{},"select = [\n",[537,2953,2954],{"class":539,"line":705},[537,2955,2956],{},"    \"E\",   # pycodestyle errors\n",[537,2958,2959],{"class":539,"line":710},[537,2960,2961],{},"    \"W\",   # pycodestyle warnings\n",[537,2963,2964],{"class":539,"line":716},[537,2965,2966],{},"    \"F\",   # pyflakes\n",[537,2968,2969],{"class":539,"line":722},[537,2970,2971],{},"    \"I\",   # isort\n",[537,2973,2974],{"class":539,"line":728},[537,2975,2976],{},"    \"C\",   # flake8-comprehensions\n",[537,2978,2979],{"class":539,"line":734},[537,2980,2981],{},"    \"B\",   # flake8-bugbear\n",[537,2983,2984],{"class":539,"line":739},[537,2985,2986],{},"    \"UP\",  # pyupgrade\n",[537,2988,2989],{"class":539,"line":745},[537,2990,2261],{},[537,2992,2993],{"class":539,"line":751},[537,2994,2995],{},"ignore = [\n",[537,2997,2998],{"class":539,"line":757},[537,2999,3000],{},"    \"E501\",  # line too long, handled by black\n",[537,3002,3003],{"class":539,"line":763},[537,3004,3005],{},"    \"B008\",  # do not perform function calls in argument defaults\n",[537,3007,3008],{"class":539,"line":769},[537,3009,3010],{},"    \"C901\",  # too complex\n",[537,3012,3013],{"class":539,"line":775},[537,3014,3015],{},"    \"W191\",  # indentation contains tabs\n",[537,3017,3018],{"class":539,"line":781},[537,3019,2261],{},[537,3021,3022],{"class":539,"line":787},[537,3023,3024],{},"exclude = [\n",[537,3026,3027],{"class":539,"line":793},[537,3028,3029],{},"    \"tests/*\",\n",[537,3031,3032],{"class":539,"line":799},[537,3033,3034],{},"    \"venv/*\",\n",[537,3036,3037],{"class":539,"line":805},[537,3038,3039],{},"    \"*/site-packages/*\",\n",[537,3041,3042],{"class":539,"line":811},[537,3043,3044],{},"    \"*/static/*\",\n",[537,3046,3047],{"class":539,"line":816},[537,3048,3049],{},"    \"wsgi.py\",\n",[537,3051,3052],{"class":539,"line":822},[537,3053,2261],{},[537,3055,3056],{"class":539,"line":827},[537,3057,3058],{},"fixable = [\"ALL\"]\n",[537,3060,3061],{"class":539,"line":832},[537,3062,3063],{},"unfixable = []\n",[537,3065,3066],{"class":539,"line":837},[537,3067,584],{"emptyLinePlaceholder":200},[537,3069,3070],{"class":539,"line":842},[537,3071,3072],{},"[tool.ruff.lint.per-file-ignores]\n",[537,3074,3075],{"class":539,"line":847},[537,3076,3077],{},"\"tests/**/*\" = [\"S101\"]  # 測試中允許 assert\n",[537,3079,3080],{"class":539,"line":853},[537,3081,584],{"emptyLinePlaceholder":200},[537,3083,3084],{"class":539,"line":858},[537,3085,3086],{},"[tool.ruff.format]\n",[537,3088,3089],{"class":539,"line":864},[537,3090,3091],{},"quote-style = \"double\"\n",[537,3093,3094],{"class":539,"line":870},[537,3095,3096],{},"indent-style = \"space\"\n",[11,3098,3100],{"id":3099},"_5-安裝依賴","5. 安裝依賴",[529,3102,3104],{"className":1231,"code":3103,"language":1233,"meta":185,"style":185},"# 這會產生 uv.lock，第一次加上 --frozen 就好\nuv sync --frozen\n",[115,3105,3106,3111],{"__ignoreMap":185},[537,3107,3108],{"class":539,"line":540},[537,3109,3110],{"class":2592},"# 這會產生 uv.lock，第一次加上 --frozen 就好\n",[537,3112,3113,3115,3118],{"class":539,"line":186},[537,3114,2097],{"class":1240},[537,3116,3117],{"class":1244}," sync",[537,3119,3120],{"class":1251}," --frozen\n",[11,3122,3124],{"id":3123},"_6-ide-設置如果你使用-vs-code","6. IDE 設置（如果你使用 VS Code）",[1768,3126,3127,3130,3133],{},[103,3128,3129],{},"安裝 VS Code extensions（如果你使用 VS Code，可以去找一下 ruff 的 extension，可以使用 IDE 的修復輔助）",[103,3131,3132],{},"Ctrl + Shift + P：>Format Document With… 將 ruff 設為 default formatter",[103,3134,3135],{},"確保 format on save = true",[11,3137,3139],{"id":3138},"_7-清理現有代碼","7. 清理現有代碼",[529,3141,3143],{"className":1231,"code":3142,"language":1233,"meta":185,"style":185},"# 先看有幾個錯誤，以及可以使用 --fix 自動修復的項目\nruff check\n\n# 自動修復\nruff check --fix\n\n# 接下來開始清理 code style 問題\n# 啟用 hot reload，可以快速修復已存在的錯誤\nruff check --watch\n",[115,3144,3145,3150,3157,3161,3166,3176,3180,3185,3190],{"__ignoreMap":185},[537,3146,3147],{"class":539,"line":540},[537,3148,3149],{"class":2592},"# 先看有幾個錯誤，以及可以使用 --fix 自動修復的項目\n",[537,3151,3152,3154],{"class":539,"line":186},[537,3153,2455],{"class":1240},[537,3155,3156],{"class":1244}," check\n",[537,3158,3159],{"class":539,"line":551},[537,3160,584],{"emptyLinePlaceholder":200},[537,3162,3163],{"class":539,"line":557},[537,3164,3165],{"class":2592},"# 自動修復\n",[537,3167,3168,3170,3173],{"class":539,"line":563},[537,3169,2455],{"class":1240},[537,3171,3172],{"class":1244}," check",[537,3174,3175],{"class":1251}," --fix\n",[537,3177,3178],{"class":539,"line":569},[537,3179,584],{"emptyLinePlaceholder":200},[537,3181,3182],{"class":539,"line":575},[537,3183,3184],{"class":2592},"# 接下來開始清理 code style 問題\n",[537,3186,3187],{"class":539,"line":581},[537,3188,3189],{"class":2592},"# 啟用 hot reload，可以快速修復已存在的錯誤\n",[537,3191,3192,3194,3196],{"class":539,"line":587},[537,3193,2455],{"class":1240},[537,3195,3172],{"class":1244},[537,3197,3198],{"class":1251}," --watch\n",[11,3200,3202],{"id":3201},"_8-執行專案","8. 執行專案",[529,3204,3206],{"className":1231,"code":3205,"language":1233,"meta":185,"style":185},"uv run pytest\nuv run python src/main.py\n",[115,3207,3208,3217],{"__ignoreMap":185},[537,3209,3210,3212,3214],{"class":539,"line":540},[537,3211,2097],{"class":1240},[537,3213,1248],{"class":1244},[537,3215,3216],{"class":1244}," pytest\n",[537,3218,3219,3221,3223,3225],{"class":539,"line":186},[537,3220,2097],{"class":1240},[537,3222,1248],{"class":1244},[537,3224,2746],{"class":1244},[537,3226,3227],{"class":1244}," src/main.py\n",[11,3229,3231],{"id":3230},"_9-dockerfile-範例","9. Dockerfile 範例",[529,3233,3237],{"className":3234,"code":3235,"language":3236,"meta":185,"style":185},"language-dockerfile shiki shiki-themes github-light github-dark","# 使用官方 Python 基礎映像\nFROM python:3.11-slim\n\n# 設定工作目錄\nWORKDIR /app\n\n# 安裝 uv\nCOPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/\n\n# 複製依賴文件\nCOPY pyproject.toml uv.lock ./\n\n# 安裝依賴（不包含開發依賴）\nRUN uv sync --frozen --no-dev\n\n# 複製應用程式代碼\nCOPY . .\n\n# 設定 Python 路徑\nENV PATH=\"/app/.venv/bin:$PATH\"\n\n# 暴露端口（根據你的應用程式調整）\nEXPOSE 8000\n\n# 執行應用程式\nCMD [\"python\", \"src/main.py\"]\n","dockerfile",[115,3238,3239,3244,3249,3253,3258,3263,3267,3271,3276,3280,3285,3290,3294,3299,3304,3308,3313,3318,3322,3327,3332,3336,3341,3346,3350,3355],{"__ignoreMap":185},[537,3240,3241],{"class":539,"line":540},[537,3242,3243],{},"# 使用官方 Python 基礎映像\n",[537,3245,3246],{"class":539,"line":186},[537,3247,3248],{},"FROM python:3.11-slim\n",[537,3250,3251],{"class":539,"line":551},[537,3252,584],{"emptyLinePlaceholder":200},[537,3254,3255],{"class":539,"line":557},[537,3256,3257],{},"# 設定工作目錄\n",[537,3259,3260],{"class":539,"line":563},[537,3261,3262],{},"WORKDIR /app\n",[537,3264,3265],{"class":539,"line":569},[537,3266,584],{"emptyLinePlaceholder":200},[537,3268,3269],{"class":539,"line":575},[537,3270,2593],{},[537,3272,3273],{"class":539,"line":581},[537,3274,3275],{},"COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/\n",[537,3277,3278],{"class":539,"line":587},[537,3279,584],{"emptyLinePlaceholder":200},[537,3281,3282],{"class":539,"line":593},[537,3283,3284],{},"# 複製依賴文件\n",[537,3286,3287],{"class":539,"line":599},[537,3288,3289],{},"COPY pyproject.toml uv.lock ./\n",[537,3291,3292],{"class":539,"line":605},[537,3293,584],{"emptyLinePlaceholder":200},[537,3295,3296],{"class":539,"line":611},[537,3297,3298],{},"# 安裝依賴（不包含開發依賴）\n",[537,3300,3301],{"class":539,"line":617},[537,3302,3303],{},"RUN uv sync --frozen --no-dev\n",[537,3305,3306],{"class":539,"line":623},[537,3307,584],{"emptyLinePlaceholder":200},[537,3309,3310],{"class":539,"line":629},[537,3311,3312],{},"# 複製應用程式代碼\n",[537,3314,3315],{"class":539,"line":635},[537,3316,3317],{},"COPY . .\n",[537,3319,3320],{"class":539,"line":641},[537,3321,584],{"emptyLinePlaceholder":200},[537,3323,3324],{"class":539,"line":647},[537,3325,3326],{},"# 設定 Python 路徑\n",[537,3328,3329],{"class":539,"line":652},[537,3330,3331],{},"ENV PATH=\"/app/.venv/bin:$PATH\"\n",[537,3333,3334],{"class":539,"line":658},[537,3335,584],{"emptyLinePlaceholder":200},[537,3337,3338],{"class":539,"line":664},[537,3339,3340],{},"# 暴露端口（根據你的應用程式調整）\n",[537,3342,3343],{"class":539,"line":670},[537,3344,3345],{},"EXPOSE 8000\n",[537,3347,3348],{"class":539,"line":676},[537,3349,584],{"emptyLinePlaceholder":200},[537,3351,3352],{"class":539,"line":682},[537,3353,3354],{},"# 執行應用程式\n",[537,3356,3357],{"class":539,"line":687},[537,3358,3359],{},"CMD [\"python\", \"src/main.py\"]\n",[2451,3361],{},[2088,3363,1627],{"id":1627},[100,3365,3366,3373,3380],{},[103,3367,3368],{},[53,3369,3372],{"href":3370,"rel":3371},"https://astral.sh/",[57],"Astral: High-performance Python tooling",[103,3374,3375],{},[53,3376,3379],{"href":3377,"rel":3378},"https://docs.astral.sh/uv/",[57],"uv Documentation",[103,3381,3382],{},[53,3383,3386],{"href":3384,"rel":3385},"https://docs.astral.sh/ruff/",[57],"Ruff Documentation",[1652,3388,3389],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":185,"searchDepth":186,"depth":186,"links":3391},[3392,3393,3397,3398,3399,3400,3401,3403,3404,3405,3406,3407],{"id":2108,"depth":186,"text":2109},{"id":2142,"depth":186,"text":2143,"children":3394},[3395,3396],{"id":2193,"depth":551,"text":2194},{"id":2425,"depth":551,"text":2425},{"id":2466,"depth":186,"text":2467},{"id":2581,"depth":186,"text":2582},{"id":2623,"depth":186,"text":2624},{"id":2728,"depth":186,"text":2729},{"id":2807,"depth":186,"text":3402},"4. 設定 pyproject.toml",{"id":3099,"depth":186,"text":3100},{"id":3123,"depth":186,"text":3124},{"id":3138,"depth":186,"text":3139},{"id":3201,"depth":186,"text":3202},{"id":3230,"depth":186,"text":3231},"介紹 uv 和 Ruff，兩個最佳的 Python 工具鏈，以及如何遷移到 uv 和 Ruff。",{},"/blog/uv-and-ruff",{"title":2084,"description":3408},"blog/uv-and-ruff",[208,3414,3415,2097,3416],"Tooling","Development","Ruff","-zRvt4hekmdP0cJ82m1OICrMfHIMFKCtweqM3DQ743s",1778576744876]