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